【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等函数可以实现该程序(流程和前边类似)。

posted on 2022-10-04 01:30  bdy  阅读(293)  评论(0编辑  收藏  举报

导航