当前位置: 首页 > news >正文

【Linux】进程间通信--共享内存

目录

简介

共享内存的实现

共享内存实例

共享内存方式(共享内存分类)

共享内存同步的四种方法

方法一、利用POSIX有名信号灯实现共享内存的同步

方法二、利用POSIX无名信号灯实现共享内存的同步(线程)

方法三、利用System V的信号灯实现共享内存的同步

方法四、利用信号实现共享内存的同步


转自:http://home.eeworld.com.cn/my/space-uid-138607-blogid-218893.html

简介

     可以说,共享内存是一种最为高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制。为了在多个进程间交换信息,内核专门留出了一块内存区,这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。因此,进程就可以直接读写这一内存区而不需要进行数据的复制,从而大大提高了效率。

当然,由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。

其原理示意图如图1所示。


图1  共享内存原理示意图

共享内存的实现

 共享内存的实现分为两个步骤:

第一步是创建共享内存,这里用到的函数是shmget(),也就是从内存中获得一段共享内存区域;

第二步是映射共享内存,也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat()。

到这里,就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作。除此之外,还有撤销映射的操作,其函数为shmdt()。这里主要介绍这3个函数。

表1 列举了shmget()函数的语法要点。

表1  shmget()函数语法要点
所需头文件
#include<sys/shm.h>
函数原型
int shmget(key_t key, int size, int shmflg)
函数传入值
key:共享内存的键值,多个进程可以通过它访问同一个共享内存,其中有个特殊值IPC_PRIVATE,用于创建当前进程的私有共享内存
size:共享内存区大小
shmflg:同open()函数的权限位,也可以用八进制表示法
函数返回值
成功:共享内存段标识符
出错:-1
 表2列举了shmat()函数的语法要点。
表2  shmat()函数语法要点
所需头文件
#include<sys/shm.h>
函数原型
char *shmat(int shmid, const void *shmaddr, int shmflg)
函数传入值
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为0则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
shmflg
SHM_RDONLY:共享内存只读
默认0:共享内存可读写
函数返回值
成功:被映射的段地址
出错:-1
  表3列举了shmdt()函数的语法要点。
表3  shmdt()函数语法要点
所需头文件
#include<sys/shm.h>
函数原型
int shmdt(const void *shmaddr)
函数传入值
shmaddr:被映射的共享内存段地址
函数返回值
成功:0
出错:-1

共享内存实例

我创建了一个结构体,想让结构体存入共享内存。写了两个程序,service和client,代码基本相同,不同就是service程序的开始创建共享内存。这两个程序是一个死循环,让你选择是存数据还是读数据还是销毁共享内存。代码写的不是很精致,主要是为了练共享内存,见谅哈。

command.c文件,构造想存入的结构体,和共享内存的操作函数

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/shm.h>
#include "Command.h"
int sharememory(int ipc_size,int flag)
{
	int id;
	key_t key=ftok("/tmp",66);
	if(key < 0)
	{
		printf("get key error\n");
		return -1;
	}
	id = shmget(key,ipc_size,flag);
	if(id < 0)
	{
		printf("get id error\n");
		return -1;
	}
	return id;
}
 
int create_ipc(int ipc_size)
{
	return sharememory(ipc_size,IPC_CREAT|IPC_EXCL|0666);
}
int get_ipc(int ipc_size)
{
	return sharememory(ipc_size,IPC_CREAT|0666);
}
int destroy_sharememory(int id)
{
	return shmctl(id,IPC_RMID,NULL);
}

command.h文件。好让service和client调用嘛,方便。

#define NAME_LEN 20
typedef struct {
	char name[NAME_LEN];
	int age;
}ckx;
int sharememory(int ipc_size,int flag);
int create_ipc(int ipc_size);
int get_ipc(int ipc_size);

service.c文件。创建共享内存空间啦,读写等

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include "Command.h"
 
int main()
{
	int id=create_ipc(sizeof(ckx));
	int i=0;
	ckx *p;
	if(id < 0)
	{
		printf("create sharememory error\n");
		return 0;
	}
	id = 0;
	while(1)
	{
		printf("\n\n1.input data to sharememory\n2.get sharememory data\n\
3.destroy sharememory\ninput select:");
		scanf("%d",&i);
		if(i > 3 |i< 1)
		{
			printf("input error\n");
			continue;
		}
		
		id = get_ipc(sizeof(ckx));
		if(id < 0)
		{
			printf("get sharememory error\n");
			break;
		}
		p = (ckx *)shmat(id,NULL,0);
		if(p < 0)
		{
			printf("get sharememory addr error\n");
			p = NULL;
			break;
		}
		
		if(i == 1)
		{
			char name[NAME_LEN];
			int age=0;
			
			printf("input name:");
			fflush(stdin);
			getchar();
			gets(name);
			printf("input age:");
			scanf("%d",&age);
			
			strcpy(p->name,name);
			p->age = age;
			printf("write success\n");
 
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
			}
			id = 0;
		}
		if(i ==  2)
		{
			printf("name:%s \t age:%d\n",p->name,p->age);
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
				break;
			}
			id = 0;
		}
		if(i == 3)
		{
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
				break;
			}
			break;
		}
	}
	if(id !=0)
	{
		if(destroy_sharememory(id)<0)
		{
			printf("destroy error\n");
		}
	}
}

client.c基本上就和service.c代码差不多啦,只是想体现共享内存嘛,service读写和client读写,观察现象,体现共享内存

#include<stdio.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include "Command.h"
 
int main()
{
	int i=0;
	ckx *p;
	int id = 0;
	while(1)
	{
		printf("\n\n1.input data to sharememory\n2.get sharememory data\n\
3.destroy sharememory\ninput select:");
		scanf("%d",&i);
		if(i > 3 |i< 1)
		{
			printf("input error\n");
			continue;
		}
		
		id = get_ipc(sizeof(ckx));
		if(id < 0)
		{
			printf("get sharememory error\n");
			break;
		}
		p = (ckx *)shmat(id,NULL,0);
		if(p < 0)
		{
			printf("get sharememory addr error\n");
			p = NULL;
			break;
		}
		
		if(i == 1)
		{
			char name[NAME_LEN];
			int age=0;
			fflush(stdin);
			getchar();
			printf("input name:");
			gets(name);
			printf("input age:");
			scanf("%d",&age);
			
			strcpy(p->name,name);
			p->age = age;
			printf("write success\n");
 
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
			}
			id = 0;
		}
		if(i ==  2)
		{
			printf("name:%s \t age:%d\n",p->name,p->age);
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
				break;
			}
			id = 0;
		}
		if(i == 3)
		{
			if(shmdt(p) == -1)
			{
				printf("shmdt error\n");
				break;
			}
			break;
		}
	}
	if(id !=0)
	{
		if(destroy_sharememory(id)<0)
		{
			printf("destroy error\n");
		}
	}
	
	
}

https://blog.csdn.net/qq_27664167/article/details/81277096

共享内存方式(共享内存分类)

1、POSIX共享内存对象

特点:

  • 访问速度非常快。
  • 进程重启共享内存中数据不会丢失,即持续性

2、POSIX内存映射文件

特点:

  • 访问速度比内存区对象慢。
  • 持续性同上

3、SYSTEM共享内存

特点:

  • 访问速度非常快
  • 持续性同上

1. POSIX共享内存对象(POSIX共享内存区大小可以在任何时刻通过ftruncate修改)

const char shmfile[] = "/tmp";
const int size = 100;

shm_open创建一个名称为tmp,大小为100字节的共享内存区对象后,在/dev/shm/下可以看到对应的文件,cat可以看到内容。

root:/home/#ls -al /dev/shm/tmp 
-rw------- 1 root root 100 10-15 13:37 /dev/shm/tmp

访问速度:非常快,因为 /dev/shm 是tmpfs的文件系统, 可以看成是直接对内存操作的,速度当然是非常快的。

持续性:随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shm_unlink或rm掉文件删除后丢失。

2.  POSIX内存映射文件

const char shmfile[] = "./tmp.shm";
const int size = 100;

open在指定目录下创建指定名称后文件,cat可以看到内容。

root:/home/#ls -al ./tmp.shm

-rw-------  1 root    root    100 10-15 13:42 tmp.shm

访问速度:慢于内存区对象,因为内核为同步或异步更新到文件系统中,而内存区对象是直接操作内存的。

持续性:随文件,即进程重启或内核自举不后丢失,除失显示rm掉文件后丢失。

3. SYSTEM V共享内存(共享内存区对象的大小是在调用shmget创建时固定下来的)

共享内存创建后,执行ipcs命令,会打印出相应的信息,比如下面所示,key为申请时分配的,可以执行ipcrm -M 0x12345678 删除,nattch字段为1表示有一个进程挂载了该内存。

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status     
0x12345678 32769      root      644        10         1

访问速度:非常快,可以理解为全内存操作。

持续性: 随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shmdt或使用ipcrm删除后丢失。

与POSIX V共享内存区对象不同的是,SYSTEM V的共享内存区对象的大小是在调用shmget创建时固定下来的,而POSIX共享内存区大小可以在任何时刻通过ftruncate修改。

【代码示例】

下面给出三种共享内存使用方法的示例代码,都采用父子进程间通讯,并未考虑互斥,仅做示例供大家参考。

1.POSIX共享内存对象

/* 
 * Posix shared memory is easy to use in Linux 2.6, in this program, we  
 * shared a memory between parent process and child process, stored several 
 * objects of struct namelist in it. We store number of items in ptr[0]. 
 */  

#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <errno.h>  

 
#define FILE_MODE (S_IRUSR | S_IWUSR)  
const char shmfile[] = "/tmp";  
const int size = 100;  

struct namelist   
{  

 int  id;   
 char name[20];  

};  

  
int main(void)  

{  

 int fd, pid, status;   
 int *ptr;  
 struct stat stat;  
  
 // create a Posix shared memory  
 int flags = O_RDWR | O_CREAT;  
 fd = shm_open(shmfile, flags, FILE_MODE);  

    if (fd < 0) {  

        printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);  
        return 0;  

    }  

 ftruncate(fd, size);  
 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
 pid = fork();  

 if (pid == 0) { 
// child process  
  printf("Child %d: start/n", getpid());  

  fd = shm_open(shmfile, flags, FILE_MODE);  
  fstat(fd, &stat);    
  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  close(fd);  

  struct namelist tmp;  

  // store total num in ptr[0];  

  *ptr = 3;  

  ptr++;  

  namelist *cur = (namelist *)ptr;  

  // store items  

  tmp.id = 1;  

  strcpy(tmp.name, "Nellson");  

  *cur++ = tmp;  

  tmp.id = 2;  

  strcpy(tmp.name, "Daisy");  

  *cur++ = tmp;  

  tmp.id = 3;  

  strcpy(tmp.name, "Robbie");  

  *cur++ = tmp;  


  exit(0);  

 } else{ // parent process  

  sleep(1);  

  struct namelist tmp;  

  int total = *ptr;  
  printf("/nThere is %d item in the shm/n", total);   

  ptr++;  
  namelist *cur = (namelist *)ptr;  

  for (int i = 0; i< total; i++) {  

   tmp = *cur;  
   printf("%d: %s/n", tmp.id, tmp.name);  
   cur++;  

  }  

  printf("/n");  

  waitpid(pid, &status, 0);  

 }  

  
 // remvoe a Posix shared memory from system  
 printf("Parent %d get child status:%d/n", getpid(), status);  

 return 0;  

}
  

编译执行

root:/home/ftpuser/ipc#g++ -o shm_posix -lrt shm_posix.cc      
root:/home/ftpuser/ipc#./shm_posix 
Child 2280: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2279 get child status:0

2.POSIX文件映射

/* 
 * Posix shared memory is easy to use in Linux 2.6, in this program, we  
 * shared a memory between parent process and child process, stored several 
 * objects of struct namelist in it. We store number of items in ptr[0]. 
 */  
  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
  
#define FILE_MODE (S_IRUSR | S_IWUSR)  
  
const char shmfile[] = "./tmp.shm";  
const int size = 100;  
  
struct namelist   
{  
 int  id;   
 char name[20];  
};  
  
int main(void)  
{  
 int fd, pid, status;   
 int *ptr;  
 struct stat stat;  
    
 // create a Posix shared memory  
 int flags = O_RDWR | O_CREAT;  
 fd = open(shmfile, flags, FILE_MODE);  
 ftruncate(fd, size);  
 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  
 pid = fork();  
 if (pid == 0) { // child process  
  printf("Child %d: start/n", getpid());  
    
  fd = open(shmfile, flags, FILE_MODE);  
  fstat(fd, &stat);    
  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  close(fd);  
  struct namelist tmp;  
  
  // store total num in ptr[0];  
  *ptr = 3;  
    
  ptr++;  
  namelist *cur = (namelist *)ptr;  
  
  // store items  
  tmp.id = 1;  
  strcpy(tmp.name, "Nellson");  
  *cur++ = tmp;  
  tmp.id = 2;  
  strcpy(tmp.name, "Daisy");  
  *cur++ = tmp;  
  tmp.id = 3;  
  strcpy(tmp.name, "Robbie");  
  *cur++ = tmp;  
  
  exit(0);  
 } else{ // parent process  
  sleep(1);  
  struct namelist tmp;  
  
  int total = *ptr;  
  printf("/nThere is %d item in the shm/n", total);   
    
  ptr++;  
  namelist *cur = (namelist *)ptr;  
  
  for (int i = 0; i< total; i++) {  
   tmp = *cur;  
   printf("%d: %s/n", tmp.id, tmp.name);  
   cur++;  
  }  
    
  printf("/n");  
  waitpid(pid, &status, 0);  
 }  
 printf("Parent %d get child status:%d/n", getpid(), status);  
 return 0;  
}

编译执行

root:/home/ftpuser/ipc#g++ -o map_posix map_posix.cc 
root:/home/ftpuser/ipc#./map_posix 
Child 2300: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2299 get child status:0

3.SYSTEM V 共享内存对象

/* 

 * System V shared memory in easy to use in Linux 2.6, in this program, we  
 * shared a memory between parent process and child process, stored several 
 * objects of struct namelist in it. We store number of items in ptr[0]. 
 */  

  

#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/shm.h>  
#include <sys/types.h>  
#include <sys/wait.h>  

#define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
const char shmfile[] = "./tmp.shm";  
const int shmsize = 10;  

struct namelist   
{  

 int  id;   
 char name[20];  

};  

  

int  main(void)  
{  

 int shmid, pid, status;   
 int *ptr;  
 struct shmid_ds buff;  

 // create a systym V shared memory  
 //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
 shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  


 pid = fork();  

 if (pid == 0) { // child process  

  printf("Child %d: start/n", getpid());  

  //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
  shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  

  ptr = (int *) shmat(shmid, NULL, 0);  

  shmctl(shmid, IPC_STAT, &buff);  

  struct namelist tmp;  

  // store total num in ptr[0];  
  *ptr = 3;  

  ptr++;  

  namelist *cur = (namelist *)ptr;  

 
  // store items  
  tmp.id = 1;  

  strcpy(tmp.name, "Nellson");  

  *cur++ = tmp;  

  tmp.id = 2;  

  strcpy(tmp.name, "Daisy");  

  *cur++ = tmp;  

  tmp.id = 3;  

  strcpy(tmp.name, "Robbie");  

  *cur++ = tmp;  

  exit(0);  

 } else{ // parent process  

  sleep(1);  

  shmctl(shmid, IPC_STAT, &buff);  

  ptr = (int *) shmat(shmid, NULL, 0);   

  struct namelist tmp;  

  int total = *ptr;  

  printf("/nThere is %d item in the shm/n", total);   

  ptr++;  

  namelist *cur = (namelist *)ptr;  

  for (int i = 0; i< total; i++) {  

   tmp = *cur;  

   printf("%d: %s/n", tmp.id, tmp.name);  

   cur++;  

  }  

  
  printf("/n");  

  waitpid(pid, &status, 0);  

 }  

 
 // remvoe a systym V shared memory from system  
 shmctl(shmid, IPC_RMID, NULL);  

 printf("Parent %d get child status:%d/n", getpid(), status);  

 return 0;  

}

编译执行

root:/home/ftpuser/ipc#g++ -o shm_v shm_v.cc  
root:/home/ftpuser/ipc#./shm_v 
Child 2323: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2322 get child status:0

【性能测试】

下面对三种方式进行性能测试,比较下差异。

测试机信息:

AMD Athlon(tm) Neo X2 Dual Core Processor 6850e

cpu:1.7G

os: Linux 2.6.18

测试方式:

打开大小为SIZE的共享内存,映射到一个int型的数组中,循环写数组、读数组。

重复10W次,计算时间开销。

内存大小

Shmopen+mmap(ms)

Open+mmap

Shmget

4k

1504

1470

1507

16k

6616

6201

5994

64k

25905

24391

24315

256k

87487

76981

69417

1M

253209

263431

241886

重复1K次,计算时间开销。

内存大小

Shmopen+mmap(ms)

Open+mmap(ms)

Shmget(ms)

1M

5458

5447

5404

4M

21492

21447

21307

16M

90880

93685

87594

32M

178000

214900

193000

分析:

Sytem V方式读写速度快于POSIX方式,而POSIX 共享内存和文件映射方式相差不大, 共享内存性能略优。

附上测试源码:

/* 
 * 共享内存读写速度测试 
 */  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <errno.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/shm.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
#include "module_call.h"  
  
#define FILE_MODE (S_IRUSR | S_IWUSR)  
#define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
  
enum emType  
{  
    SHMOPEN = 0x01,  
    OPEN = 0x02,  
    SHMGET = 0x04,  
};  
  
void * GetShmMem(emType type, int size)  
{  
    void * ptr = NULL;  
    switch (type)  
    {  
        case SHMOPEN:  
            {  
                const char shmfile[] = "/tmp";  
                int flags = O_RDWR | O_CREAT;  
                int fd = shm_open(shmfile, flags, FILE_MODE);  
                if (fd < 0)  
                {  
                    printf("shm_open failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
                    return NULL;  
                }  
                  
                ftruncate(fd, size);  
  
                ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);     
                if (MAP_FAILED == ptr)  
                {  
                    printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
                    return NULL;  
                }  
  
                break;  
            }  
        case OPEN:  
            {  
                const char shmfile[] = "./tmp.shm";  
                int flags = O_RDWR | O_CREAT;  
                int fd = open(shmfile, flags, FILE_MODE);  
                if (fd < 0)  
                {  
                    printf("ope failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
                    return NULL;  
                }  
  
                ftruncate(fd, size);  
                  
                ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                 
                if (MAP_FAILED == ptr)  
                {  
                    printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
                    return NULL;  
                }  
  
                break;  
            }  
  
        case SHMGET:  
            {  
                int shmid;    
                struct shmid_ds buff;  
                const char shmfile[] = "./tmp.shm_v";  
                shmid = shmget(ftok(shmfile, 0), size, SVSHM_MODE | IPC_CREAT);  
                if (shmid < 0)  
                {  
                    printf("shmget failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
                    return NULL;  
                }  
                  
                ptr = (int *) shmat(shmid, NULL, 0);  
                if ((void *) -1 == ptr)  
                {  
                    printf("shmat failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
                    return NULL;  
                }  
                  
                shmctl(shmid, IPC_STAT, &buff);  
                break;  
            }  
    }  
  
    return ptr;  
}  
  
int realmain(int size, int loop, emType type)  
{  
    int * array_int = NULL;  
  
    /* get shmmem*/  
    array_int = (int *)GetShmMem(type, size);  
    if (NULL == array_int)  
    {  
        printf("GetShmMem failed/n");  
        return -1;  
    }  
  
    /* loop */  
    int array_num = size/sizeof(int);  
    modulecall::call_start();  
    while (0 != loop)  
    {  
        /* write */  
        for (int i = 0; i < array_num; i++)  
        {  
            array_int[i] = i;  
        }  
  
        /* read */  
        for (int i = 0; i < array_num; i++)  
        {  
            if (array_int[i] != i)  
            {  
                printf("ShmMem is invalid i=%d v=%d/n", i, array_int[i]);  
                return -1;  
            }  
        }  
      
        loop--;  
    }  
    modulecall::call_end();  
    printf("timecost=%lld/n", modulecall::call_timecost());  
  
    return 0;  
}  
  
int main(int argc, char ** argv)  
{  
    if (argc < 4)  
    {  
        printf("usage: %s size loop shmtype(1-shmposix 2-mapposix 4-shmv 7-all)/n", argv[0]);  
        return -1;  
    }  
  
    const int size = atoi(argv[1]);  
    int loop = atoi(argv[2]);  
    const int type = atoi(argv[3]);  
  
    if ((type&SHMOPEN) == SHMOPEN)  
    {  
        printf("shmopen ");  
        realmain(size, loop, SHMOPEN);  
    }  
  
    if ((type&OPEN) == OPEN)  
    {  
        printf("open    ");  
        realmain(size, loop, OPEN);  
    }  
  
    if ((type&SHMGET) == SHMGET)  
    {  
        printf("shmget  ");  
        realmain(size, loop, SHMGET);  
    }     
  
    return 0;  
}

共享内存同步的四种方法

https://blog.csdn.net/sunxiaopengsun/article/details/79869115

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等 。

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制。信号灯包括posix有名信号灯、 posix基于内存的信号灯(无名信号灯)和System V信号灯(IPC对象)

方法一、利用POSIX有名信号灯实现共享内存的同步

有名信号量既可用于线程间的同步,又可用于进程间的同步。

两个进程,对同一个共享内存读写,可利用有名信号量来进行同步。一个进程写,另一个进程读,利用两个有名信号量semr, semw。semr信号量控制能否读,初始化为0。 semw信号量控制能否写,初始为1。

链接:https://www.jianshu.com/p/ee5bb8948dc0

示例代码如下:

//读共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main()
{
    int shmid = -1;
    key_t key = 0x2234;
    Teacher *p = NULL;
    sem_t *semr = NULL, *semw = NULL;
    semr = sem_open("sem_r", O_CREAT | O_RDWR, 0666, 0);
    if (semr == SEM_FAILED )
    {
        printf("errno = %d\n", errno );
        return -1;
    }

    semw = sem_open("sem_w", O_CREAT | O_RDWR, 0666, 1 );
    if (semw == SEM_FAILED)
    {
        printf("errno = %d\n", errno );
        return -1;
    }
    
    shmid = shmget(key, 0, 0 );
    if ( shmid == -1 )
    {
        printf("shmget failed\n");
        perror("shmget err");
        return -1;
    }

    p = (Teacher*)shmat(shmid, NULL, 0);
    if (p == (Teacher*)(-1))
    {
        printf("shmat failed\n");
        perror("shmat");
        return -1;
    }

    while(1)
    {
        sem_wait(semr);
        printf("name:%s\n", p->name);
        printf("age:%d\n", p->age);
        sem_post(semw);
    }

    //shmdt(p);
    return 0;
}
//写共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main()
{
    int shmid = -1;
    key_t key = 0x2234;
    Teacher *p = NULL;
    int count = 0;
    sem_t *semr = NULL, *semw = NULL;
    semr = sem_open("sem_r", O_CREAT | O_RDWR, 0666, 0);
    if (semr == SEM_FAILED )
    {
        printf("errno = %d\n", errno );
        return -1;
    }

    semw = sem_open("sem_w", O_CREAT | O_RDWR, 0666, 1 );
    if (semw == SEM_FAILED)
    {
        printf("errno = %d\n", errno );
        return -1;
    }
    
    shmid = shmget(key, sizeof(Teacher), 0666 | IPC_CREAT );
    if ( shmid == -1 )
    {
        perror("shmget");
        return -1;
    }

    p = (Teacher*)shmat(shmid, NULL, 0);
    if (p == (Teacher*)(-1))
    {
        perror("shmat");
        return -1;
    }

    while(1)
    {
        sem_wait(semw);
        //printf(">name:");
        strcpy(p->name, "aaaa");
        p->age = count;
        ++count;
        sem_post(semr);
    }
    return 0;
}

注意:编译上面的代码需要链接动态库-lpthread

方法二、利用POSIX无名信号灯实现共享内存的同步(线程)

POSIX无名信号量是基于内存的信号量,可以用于线程间同步也可以用于进程间同步。若实现进程间同步,需要在共享内存中来创建无名信号量。

因此,共享内存需要定义以下的结构体。

typedef struct
        {
                sem_t semr;
                sem_t semw;
                char buf[MAXSIZE];
        }SHM;

读、写程序流程如下图所示。

方法三、利用System V的信号灯实现共享内存的同步

System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯

System V 信号灯由内核维护,主要函数semget,semop,semctl 。

一个进程写,另一个进程读,信号灯集中有两个信号灯,下标0代表能否读,初始化为0。 下标1代表能否写,初始为1。

程序流程如下:

写的流程和前边的类似。

//进程A
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

int shm_id, sem_id;
char* addr;

void ser_exit(int signo)
{
    semctl(sem_id, 0, IPC_RMID);
    semctl(sem_id, 1, IPC_RMID);
    shmdt(addr);
    shmctl(shm_id, IPC_RMID, NULL);

    printf("server exit ...\n");
    exit(0);
}

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    struct sigaction act;
    act.sa_handler = ser_exit;

    key_t shm_key = ftok("./readshm", 1);
    if (shm_key == -1 )
    {
        perror("ftok error");
        return -1;
    }

    int shm_id = shmget(shm_key, 1024, IPC_CREAT | IPC_EXCL | 0755);
    if (shm_id == -1)
    {
        perror("shmget");
        return -1;
    }
    
    char* addr = (char*)shmat(shm_id, NULL, 0);
    if (addr == (char*)(-1))
    {
        perror("shmat");
        return -1;
    }

    int sem_id = semget(shm_key, 2, IPC_CREAT|IPC_EXCL|0755);
    if (sem_id == -1 )
    {
        perror("semget");
        return -1;
    }

    union semun init;
    init.val = 0;

    semctl(sem_id, 0, SETVAL, init);
    semctl(sem_id, 1, SETVAL, init);

    struct sembuf v = {0, 1, SEM_UNDO};
    struct sembuf p = {1, -1, SEM_UNDO};

    sigaction(SIGINT, &act, NULL);
    while(1)
    {
        printf("ser:>");
        scanf("%s", addr);
        semop(sem_id, &v, 1);
        semop(sem_id, &p, 1);
        printf("cli:>%s\n", addr);
    }

    return 0;
}
//进程B
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

void cli_exit(int signo)
{
    printf("client exit ...\n");
    exit(0);
}

int main()
{
    struct sigaction act;
    act.sa_handler = cli_exit;

    key_t shm_key = ftok("./readshm", 1);
    if (shm_key == -1 )
    {
        perror("ftok error");
        return -1;
    }

    int shm_id = shmget(shm_key, 0, 0);
    if (shm_id == -1)
    {
        perror("shmget");
        return -1;
    }
    
    char* addr = (char*)shmat(shm_id, NULL, 0);
    if (addr == (char*)(-1))
    {
        perror("shmat");
        return -1;
    }

    int sem_id = semget(shm_key, 0, 0 );
    if (sem_id == -1 )
    {
        perror("semget");
        return -1;
    }

    struct sembuf v = {1, 1, SEM_UNDO};
    struct sembuf p = {0, -1, SEM_UNDO};

    sigaction(SIGINT, &act, NULL);
    while(1)
    {
        semop(sem_id, &p, 1);
        printf("ser:>%s\n", addr );

        printf("cli:>");
        scanf("%s", addr);
        semop(sem_id, &v, 1);
    }

    return 0;
}

方法四、利用信号实现共享内存的同步

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。利用信号也可以实现共享内存的同步。

思路:

reader和writer通过信号通信必须获取对方的进程号,可利用共享内存保存双方的进程号。

reader和writer运行的顺序不确定,可约定先运行的进程创建共享内存并初始化。

利用pause, kill, signal等函数可以实现该程序(流程和前边类似)。

相关文章:

  • 【open stack】openstack从入门到放弃
  • 【防火墙】linux打开防火墙的某几个端口|打开/关闭防火墙centos7
  • 【web.xml】web.xml文件的作用和servlet 、servlet-mapping 标签
  • 【JSON和JSONP】浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
  • 【JSP、servlet、SQL】JSP、servlet、SQL三者之间的数据传递
  • 【Jackson】Jackson和Jackson的用法实例分析
  • 【Spring】Spring 基础,用小例子来理解它(spring boot)
  • 【JQuery】jquery通过ajax方法获取json数据不执行success
  • 【jQuery】jQuery中$.get、$.post、$.getJSON和$.ajax用法的区别
  • 【json序列化】json的序列化、二进制序列化、JSON传输二进制数据方法
  • 【MVC\MVC\MVP】MVC、MVP、MVVM的区别
  • 【三范式】数据库三范式是什么?
  • 【SSH免密码登录】SSH Secure Shell Client用public key认证登录|linux 生成密钥
  • 【div Resize】Javascript中 非Window的DOM的onresize事件解决方案
  • []新浪博客如何插入代码(其他博客应该也可以)
  • 【Linux系统编程】快速查找errno错误码信息
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • Android 控件背景颜色处理
  • Angular 2 DI - IoC DI - 1
  • CSS实用技巧
  • HomeBrew常规使用教程
  • Java方法详解
  • js面向对象
  • Laravel5.4 Queues队列学习
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • PHP 小技巧
  • React中的“虫洞”——Context
  • Swoft 源码剖析 - 代码自动更新机制
  • vue--为什么data属性必须是一个函数
  • 从零搭建Koa2 Server
  • 离散点最小(凸)包围边界查找
  • 实习面试笔记
  • 数据科学 第 3 章 11 字符串处理
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 如何在招聘中考核.NET架构师
  • #pragma预处理命令
  • (AngularJS)Angular 控制器之间通信初探
  • (C语言)逆序输出字符串
  • (floyd+补集) poj 3275
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (七)Knockout 创建自定义绑定
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)fock函数详解
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET与 java通用的3DES加密解密方法
  • .net中应用SQL缓存(实例使用)
  • @Bean注解详解
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ 数据结构 - C++]红黑树RBTree
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网