Linux下的几个IPC(信号量、共享内存、消息队列)都使用了相似的系统调用,上一章介绍了信号量,这一节来说说共享内存。
背景知识
共享内存是内核在物理内存中预留了一片空间用于进程间的通信,这部分内存空间不属于任何一个进程,但每个进程都可以访问它们(通过建立映射)。共享内存是进程间通信最简单的方式,但缺点在于没有进程间同步的功能,因此我们需要使用其他技术进行共享内存的同步。
相关系统调用
shmget()
同semget()
类似,shmget()
也是用于创建或者获取共享内存。它的函数原型如下:
1 2
| int shmget(key_t key, size_t size, int shmflg);
|
size
是我们需要共享内存的大小,shmflg
是设置共享内存的标志位。同semget()
一样,最后9位是访问权限,要创建新的共享内存需要使用shmflg | IPC_CREA | IPC_EXCL
,如果返回EEXIST
说明已经创建了同key
的共享内存。
shmat()/shmdt()
创建好共享内存后,我们就需要将其映射到物理内存空间。使用shmat()
系统调用建立映射,shmdt()
解除这种映射。shmat()
的函数原型如下:
1 2
| void *shmat(int shmid, const void *shmaddr, int shmflg);
|
shmaddr
一般设置成NULL
,这样内核会自动分配一块空间(也便于移植);shmflg
可以设置为SHM_RDONLY
、SHM_RND
等,但一般什么都不用设置,只填0就行了。
shmdt()
函数原型如下:
1 2
| int shmdt(const void *shmaddr);
|
shmdt()
函数只是断开进程和共享内存的映射,并没有销毁共享内存,要销毁共享内存需要使用函数shmctl()
。
shmctl()
shmctl()
函数原型如下:
1 2
| int shmctl(int shmid, int cmd, struct shmid_ds *buf);
|
shmctl()
可以用的命令有很多,要销毁之前创建的共享内存,我们需要使用IPC_RMID
命令。使用IPC_RMID
时,buf
参数会被忽略,因此直接使用shmctl(shmid, IPC_RMID, NULL)
即可。必须是创建或者拥有这个共享内存的进程才能调用shmctl()
来销毁它。
共享内存实例
下面这个实例来自博文 - Linux 高级编程 - 共享内存 Shared Memory,该例子没有牵涉进程同步的内容。
写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <string.h> #include <stdio.h> #include <stdlib.h>
int main() { int shm_id = shmget(13, 2048, IPC_CREAT | 0666); if (shm_id != -1) { void* shm = shmat(shm_id, NULL, 0); if (shm != (void*)-1) { char str[] = "I'm share memory"; memcpy(shm, str, strlen(str) + 1); shmdt(shm); } else { perror("shmat:"); } } else { perror("shmget:"); } return 0; }
|
读操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <string.h> #include <stdio.h> #include <stdlib.h>
int main() { int shm_id = shmget(13, 2048, IPC_CREAT | 0666); if (shm_id != -1) { void* shm = shmat(shm_id, NULL, 0); if (shm != (void*)-1) { char str[50] = { 0 }; memcpy(str, shm, strlen("I'm share memory")); printf("shm = %s\n", (char *)shm); shmdt(shm); } else { perror("shmat:"); } } else { perror("shmget:"); } if (0 == shmctl(shm_id, IPC_RMID, NULL)) printf("delete shm success.\n"); return 0; }
|