什么是共享内存?
共享内存是进程间通信的一种方式,而且是最快的IPC方式。
共享内存图解
为什么说共享内存是最快的IPC?(重点)
因为共享内存是直接将同一块物理内存映射到2个进程的虚拟地址空间当中,所以进行的数据传输的时候相较于其他通信方式,少了两步用户态到内核态数据拷贝的过程,因此称,共享内存是最快的进程间通信方式.
(像管道/消息队列等方式都是先将数据从用户态转入内核态,进行系统调用,然后再从内核态转出到用户态,让用户看见)
共享内存代码实现
使用步骤:
- 创建共享内存
- 将共享内存映射到虚拟地址空间
- 内存中进行数据操作实现进程间通信
- 使用完毕后解除映射关系,然后删除共享内存
如果有进程依然与共享内存保持映射链接关系,那么共享内存将不会被立即删除,而是等最后一个映射断开后删除,在这期间,将拒绝其他进程映射
相关函数
shmget
功能:⽤来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存⼤⼩
shmflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1
shmat
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址 可以设置为NULL
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(要么只读要么读写)
返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1
shmdt
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl
功能:⽤于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
IPC_STAT:把shmid_ds中的数据设置成当前共享内存的当前关联值
IPC_SET:在权限足够的情况下,将共享内存中的当前关联值设置成shmid_ds的数据
IPC_RMID:删除共享内存(当然要等到没有进程映射这块内存时才删除)
buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
示例:
memshare.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
6 #include <sys/ipc.h>
7 #include <sys/shm.h>
8
9 #define IPC_KEY 0x12345678
10 int main()
11 {
12 //1. 创建共享内存 13 //int shmget(key_t key, size_t size, int shmflg);
14 // key: 操作系统上ipc标识 15 // size: 要创建的共享内存大小
16 // shmflg:
17 // IPC_CREAT|IPC_EXCL|0664
18 // 返回值:操作句柄 失败:-1
19 int shmid = -1;
20 shmid = shmget(IPC_KEY, 32, IPC_CREAT|0664);
21 if (shmid < 0) {
22 perror("shmget error");
23 return -1;
24 }
25 //2. 将共享内存映射到虚拟地址空间
27 // shmid: 操作句柄
28 // shmaddr: 映射起始地址,NULL(操作系统分配)
31 void *shm_start = shmat(shmid, NULL, 0);
32 if (shm_start == (void*)-1) {
33 perror("shmat errro");
34 return -1;
35 }
36 while(1) {
37 //直接通过这个首地址操作共享内存即可
38 printf("please input:");
39 //清空一下共享内存中的数据
40 memset(shm_start, 0x00, 32);
41 scanf("%s", (char*)shm_start);
42 sleep(1);
43 }
44 //4. 不玩了 a. 解除映射 b. 删除
45 //解除映射
46 //int shmdt(const void *shmaddr);
47 // shmaddr 共享内存的映射首地址
48 // 返回值:成功:0 失败:-1
49 shmdt(shm_start);
50 //删除共享内存
51 //int shmctl(int shmid, int cmd, struct shmid_ds *b uf);
52 // shmid: 句柄
53 // cmd: IPC_RMID 删除
54 // buf: 用于接收共享内存描述信息,不关心可以置空
55 shmctl(shmid, IPC_RMID, NULL);
56 return 0;
57 }
58
memshare1.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
6 #include <sys/ipc.h>
7 #include <sys/shm.h>
8
9 #define IPC_KEY 0x12345678
10 int main()
11 {
12 //1. 创建共享内存 13 //int shmget(key_t key, size_t size, int shmflg);
14 // key: 操作系统上ipc标识 15 // size: 要创建的共享内存大小
16 // shmflg:
17 // IPC_CREAT|IPC_EXCL|0664
18 // 返回值:操作句柄 失败:-1
19 int shmid = -1;
20 shmid = shmget(IPC_KEY, 32, IPC_CREAT|0664);
21 if (shmid < 0) {
22 perror("shmget error");
23 return -1;
24 }
25 //2. 将共享内存映射到虚拟地址空间
27 // shmid: 操作句柄
28 // shmaddr: 映射起始地址,NULL(操作系统分配)
29 // shmflg: SHM_RDONLY--只读 否则读写
30 // 返回值:映射的虚拟地址空间首地址
31 void *shm_start = shmat(shmid, NULL, 0);
32 if (shm_start == (void*)-1) {
33 perror("shmat errro");
34 return -1;
35 }
36 while(1) {
37 printf("%s\n", (char*)shm_start);
38 sleep(1);
39 }
40 //4. 不玩了 a. 解除映射 b. 删除
41 //解除映射
42 //int shmdt(const void *shmaddr);
43 // shmaddr 共享内存的映射首地址
44 // 返回值:成功:0 失败:-1
45 shmdt(shm_start);
46 //删除共享内存
47 //int shmctl(int shmid, int cmd, struct shmid_ds *b uf);
48 // shmid: 句柄
49 // cmd: IPC_RMID 删除
50 // buf: 用于接收共享内存描述信息,不关心可以置空
51 shmctl(shmid, IPC_RMID, NULL);
52 return 0;
53 }
54 ```