1.共享内存
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
共享内存示意图:
在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。
2.共享内存操作过程
1.创建/打开共享内存
2.将共享内存映射到进程的虚拟地址空间
3.通过任意的内存操作,对共享内存进行操作
4.解除映射关系(共享内存会记录当前的进程映射链接数)
5.删除共享内存
3.共享内存函数
1.创建共享内存:
通过ftok生成的key
值是唯一的。
int shmget(key_t key, size_t size, int shmflg);
- 参数:
- key:这个共享内存段名字
- size:共享内存大小,size大小一般为4096的整数倍
- shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
- 返回值:
成功返回一个非负整数,即该共享内存段的标识码;失败返回-1,
这个返回值标识符是给用户使用的。而通过ftok创建的是系统识别的。
2.删除共享内存
shmctl函数:
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功返回0;失败返回-1
下图是cmd的命令
3.将共享内存段连接到进程地址空间
shmat函数:
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
说明:
- shmaddr为NULL,核心自动选择一个地址
- shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
- shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -(shmaddr % SHMLBA)
- shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
4.共享内存段与当前进程取消关联
shmdt函数
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
4.共享内存资源查看和删除
ipcs -a 是默认的输出信息 打印出当前系统中所有的进程间通信方式的信息
ipcs -m 打印出使用共享内存进行进程间通信的信息
ipcs -q 打印出使用消息队列进行进程间通信的信息
ipcs -s 打印出使用信号进行进程间通信的信息
ipcs -m //查看共享内存
ipcrm -M shmkey: 移除用shmkey创建的共享内存段
ipcrm -m shmid : 移除用shmid标识的共享内存段
ipcrm -Q msgkey : 移除用msqkey创建的消息队列
ipcrm -q msqid : 移除用msqid标识的消息队列
ipcrm -S semkey : 移除用semkey创建的信号
ipcrm -s semid : 移除用semid标识的信号
ipcrm -m shmid //
5.实现内存共享程序
共享内存,底层不提供任何同步与互斥机制。
client端:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/ipc.h>
4 #include <sys/shm.h>
5 #include "comm.h"
6 #include <unistd.h>
7
8 int main()
9 {
10 key_t k = ftok(PATHNAME, PROJ_ID);
W> 11 printf("key:%p\n", k);
12
13 int shmid = shmget(k, SIZE, IPC_CREAT);//唯一标识shmid,SIZE一般设为4096的整数倍
14
15 if(shmid < 0){
16 perror("shmget");
17 return 1;
18 }
19
20 printf("shmid :%d\n",shmid);
21
22 char *str = (char*)shmat(shmid, NULL, 0);//连接
23
24 char c = 'a';
25 for(; c <='z'; c++){
26 str[c-'a'] = c;
27 sleep(1);
28 }
29
30 shmdt(str);
31
32 sleep(5);
33 return 0;
34 }
server端:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/ipc.h>
4 #include <sys/shm.h>
5 #include "comm.h"
6 #include <unistd.h>
7
8 int main()
9 {
10 key_t k = ftok(PATHNAME, PROJ_ID);
W> 11 printf("key:%p\n", k);
12
13 int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);
14
15 if(shmid < 0){
16 perror("shmget");
17 return 1;
18 }
19
20 printf("shmid :%d\n",shmid);
21
22
23 char *str = (char*)shmat(shmid, NULL, 0);
24
25 while(1){
26 sleep(1);
27 printf("%s\n", str);
28 }
29
30 shmdt(str);
31
32 shmctl(shmid, IPC_RMID, NULL);
33
34 return 0;
35 }
运行:
6.总结
- 内存共享,只需要发生一次拷贝,速度是进程通信之中最快的
- 内存共享之中的key是OS之中共享内存的唯一标识,shmid是用户层面的定位标识
- IPC资源的生命周期随内核,只要不删除,就一直存在于内核中,除非重启系统
- 共享内存只有在当前映射链接数为0时,才会真正被删除。