进程间通信——System V IPC与共享内存

  什么是System V IPC?

  System V是一种Unix操作系统,此系统引入了三种高级进程间的通信机制:消息队列,共享内存,信号量。System V IPC是以下三种IPC的合称:

  System V 消息队列     System V 信号量      System V 共享内存

  消息队列、信号量、共享内存也称为IPC对象。通过ipcs可以查看当前的IPC对象,通过ipcrm可以删除IPC对象。

1   ipcs -q: 只显示消息队列。
2   ipcs -s: 只显示信号量。
3   ipcs -m: 只显示共享内存。
4   ipcs –help: 其他的参数

  IPC对象存在于内核中而不是文件系统中,由用户控制释放,不像管道的释放由内核控制

       IPC对象通过其标识符来引用和访问,所有IPC对象在内核空间有唯一性标志ID,在用户空间的唯一性标识符称为key

  IPC对象函数创建

进程间通信——System V IPC与共享内存

  Unix系统中,一切皆文件,很多IPC机制中的操作都是针对文件描述符的,但system V是针对IPC对象的ID操作的,而ID(标识符)是由key(键)生成的。

  ftok() ---> key ------>  ID

  接下来看一下ftok函数:

  ftok()函数

1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 
4 key_t ftok(const char *pathname, int proj_id);
5 //pathname为文件路径名,必须存在且可访问,常为当前路径名“.”
6 //proj_id 子ID,用于生成key的数字,范围1-255
7 

  System V IPC详见:https://blog.csdn.net/qq_38211852/article/details/80475818

 

共享内存

  共享内存允许不同的进程访问同一个逻辑内存,对内存直接读写,而不需要任何数据的拷贝,对于消息队列、管道等通信方式,需要在内核空间进行四次数据拷贝,而高兴内存只需要拷贝两次,一次输入文件到共享内存区,另一次从共享内存区到输出文件。。

  共享内存在内核空间创建,可以被映射到用户空间访问,使用灵活。(映射即建立关联:如在内核中一块内存地址为1,通过映射到用户空间后为a,之后在用户空间访问a即访问内核空间1一样。)

  共享内存并未提供同步机制,所以需要用其他机制实现不同进程对共享内存的访问。

    共享内存使用步骤

1 创建/打开共享内存
2 映射共享内存,即把指定的共享内存映射到进程地址空间用于访问
3 读写共享内存
4 撤销共享内存映射
5 删除共享内存对象

  1、共享内存创建—shmget

 1 #include <sys/ipc.h>
 2 #include <sys/shm.h>
 3 
 4 int shmget(key_t key, size_t size, int shmflg);
 5 
 6 //成功返回共享内存的ID,失败返回EOF
 7 //参数:
 8 //key---可由fotk生成,为共享内存段命令,函数返回与key相关标识符
 9 //size---以字节为单位指定需要的共享内存容量
10 //shmflg---权限标志,如:IPC_CREAT|0666

  示例1:创建一个私有的共享内存,大小为512字节,权限为0666

1 int shmid;
2 
3 if((shmid = shmget(IPC_PRIVATE, 512, 0666)) < 0)
4 {
5       perror("shmget");
6       exit(-1);            
7 }

  示例2:创建/打开一个和KEY关联的共享内存,大小为1024字节,权限为0666

 1 key_t key;
 2 int shmid;
 3 
 4 if((key = ftok(".", 'm')) == -1)
 5 {
 6      perror("ftok");
 7      exit(-1);
 8 }
 9 if((shmid = shmget(key, 1024, IPC_CREAT|0666)) < 0)
10 {
11       perror("shmget");
12       exit(-1);            
13 }

 

  2、共享内存的映射—shmat

 1 #include <sys/ipc.h>
 2 #include <sys/shm.h>
 3 
 4 void *shmat(int shmid, const void *shmaddr, int shmflg);
 5 
 6 //成功返回映射后的地址,失败返回(void *)-1
 7 //参数:
 8 //shmid---要映射的共享内存ID
 9 //shmaddr---映射后的地址,NULL表示由系统自动映射
10 //shmflg---标志位,0表可读写;SHM_RDONLY表只读

  shmat--share memory attach,应用层无法直接访问内核内存,通过系统调用,将内核中的共享内存映射到用户空间。

  3、写共享内存

  通过指针访问共享内存,指针类型取决于共享内存中存放的数据类型

  示例:在共享内存中存放键盘输入的字符串

 1 char *addr;
 2 int shmid;
 3 ...
 4 if((addr = (char *)shmat(shmid, NULL, 0)) == (char *)-1)
 5 {
 6       perror("shmat");
 7       exit(-1);  
 8 }
 9 fgets(addr, N, stdin);
10 ...

 

  4、共享内存撤销映射—shmdt

  将共享内存从当前进程中分离,分离并不是删除,只是使该共享内存对当前进程不再可用。

 1 #include <sys/ipc.h>
 2 #include <sys/shm.h>
 3 
 4 int shmdt(const void *shmaddr);
 5 
 6 //成功时返回0,失败时返回EOF
 7 //参数:
 8 //shmaddr---为shmat函数返回的地址指针
 9 //不使用共享内存时应当撤销映射
10 //进程结束时会自动撤销

 

  5、共享内存控制—shmctl

  用于控制共享内存

 1 #include <sys/ipc.h>
 2 #incude  <sys/shm.h>
 3 
 4 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
 5 
 6 //成功返回0,失败返回EOF
 7 //参数
 8 //shmid---要操作的共享内存id
 9 //cmd---要执行的操作  
10 //       IPC_STAT:用共享内存的当前关联值覆盖shmid_ds的值。
11 //       IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
12 //       IPC_REID:删除共享内存段

 

注意事项:

  每块共享内存的大小有限制,可以通过下命令查看

  ipcs -l

  cat /proc/sys/kernel/shmmax

  共享内存删除的时间点

  shmctl(shmid, IPC_RMID, NULL);添加删除标记

  当nattach变成0时才真正删除。

测试程序

通过shm_write 和shm_read这两个进程实现对共享内存的读写

shm_wirte.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <sys/ipc.h>
 5 #include <sys/shm.h>
 6 
 7 int main()
 8 {
 9     key_t key;
10     int shmid;
11     char *addr; //定义映射后的地址
12     key = ftok(".",23);
13     if(key == -1)
14     {
15         perror("ftok");
16         return -1;
17     }
18     shmid = shmget(key,1024,IPC_CREAT|0666); //创建共享内存
19     if(shmid==-1)
20     {
21         perror("shmget");
22         return -1;
23     }
24     
25     addr = shmat(shmid,NULL,0); //映射共享内存
26 
27     strcpy(addr,"this is share memory"); //读写共享内存
28 
29     shmdt(addr); //撤销共享内存映射
30 
31 
32 }

shm_read.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <sys/ipc.h>
 5 #include <sys/shm.h>
 6 
 7 int main()
 8 {
 9     key_t key;
10     int shmid;
11     char *addr; //定义映射后的地址
12     key = ftok(".",23);
13     if(key == -1)
14     {
15         perror("ftok");
16         return -1;
17     }
18     shmid = shmget(key,1024,IPC_CREAT|0666); //创建共享内存
19     if(shmid==-1)
20     {
21         perror("shmget");
22         return -1;
23     }
24     
25     addr = shmat(shmid,NULL,0); //映射共享内存
26 
27 //    strcpy(addr,"this is share memory"); //读写共享内存
28     printf("get share memory = %s\n",addr);
29     shmdt(addr); //撤销共享内存映射
30 
31 
32 }

结果:

进程间通信——System V IPC与共享内存

上一篇:c#获取本地IP地址


下一篇:IPC通信_消息队列