什么是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对象函数创建
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 }
结果: