-
共享内存允许多个进程共享一个给定的内存空间,进程可以直接读写内存,因此是IPC中速度最快的。
-
Linux中,内核专门留出了一块内存区作为共享内存区,用于多个进程交换信息。需要通信的进程将共享内存区映射到自己的私有地址空间,从而使读写进程地址空间就相当于读写内存区。使用共享内存的头文件是#include <sys/shm.h>
-
由于多个进程读写同一块内存区,所以需要进行同步处理,一般要和信号量联合使用(也可使用互斥量和记录锁)。
- 共享内存段默认是32M字节。
-
共享内存的操作流程(只使用于相关进程,即亲缘进程间的通信):
- 创建/打开一块共享内存区
- 把指定的共享内存映射到进程的地址空间
- 撤销共享内存映射
- 删除共享内存对象(key代表的IPC对象)
-
共享内存常用函数:
-
shmget(key, size, flag):创建新的共享内存段或者取得已有的共享内存段,函数返回共享存储段的ID(shmid)。key标识共享内存的键值,两个进程用相同的key时用shmget得到的shmid是相同的,此时可以访问同一块共享内存。size表示获得的共享内存大小。flag表示共享内存块的访问权限,如果想共享内存段不存在时新建一个,用IPC_CREAT与权限值做位与操作即可。
-
shmat(shmid, addr, flag):将共享存储段映射到进程的地址空间。应当制定addr为0,由系统选择地址(该地址位于堆栈之间)。 flag是一组标志位,一般也为0。调用成功后,返回指向共享内存第一个字节的指针。
-
shmdt(addr):将进程地址空间与该共享内存段分离,使该共享内存对当前进程而言不可用。
-
shmctl(shm_id, command, buf):控制共享内存。command参数:IPC_RMID(删除共享内存段)、IPC_STAT和IPC_SET(不常用)
-
无关进程(非亲缘进程)共享内存的方法:
- 使用XSI共享存储函数
- 使用mmap将同一文件映射到多个进程的地址空间,为此要使用MAP_SHARED标志以保证:一个进程写到存储段,另一个进程可见。
- 使用共享内存的优缺点:
- 优点:方便,接口简单;数据不用传送而是直接读写内存,效率高;没有无名管道那种亲缘进程才能通信的限制,适用于不相关进程通信。
- 缺点:需要借助外部的同步机制
- 共享内存的使用例子,创建两个进程,shmwrite向共享内存写数据,shmread从共享内存读数据:
- shmread进程,创建一块共享内存段,将共享内存段映射到自己的内存空间,从内存中读数据。
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/shm.h> struct shared_use_st { int wirte_read_flag; //作为一个标志,非0:表示可读,0表示可写 char text[1024]; //记录写入和读取的文本 }; int main() { int running = 1; //程序是否继续运行的标志 void *shm = NULL; //分配的共享内存的原始首地址 struct shared_use_st *shared; //指向shm int shmid; //共享内存标识ID号 //创建共享内存,当key一样时返回的shmid也是一样的,则两个进程使用同一块共享内存,IPC_CREAT表示创建一块指定key的共享内存块 shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT); if(shmid == -1) { fprintf(stderr, "shmget failed\n"); exit(EXIT_FAILURE); } //将共享内存连接到当前进程的地址空间 shm = shmat(shmid, 0, 0); if(shm == (void*)-1) { fprintf(stderr, "shmat failed\n"); exit(EXIT_FAILURE); } printf("\nMemory attached at %X\n", (int)shm); //设置共享内存 shared = (struct shared_use_st*)shm; shared->wirte_read_flag = 0; //设为可写 while(running) //读取共享内存中的数据 { //可读模式,从进程的地址空间shm里读就相当于从共享内存里读 if(shared->wirte_read_flag != 0) { printf("Receive Message: %s", shared->text); sleep(rand() % 3); //读取完数据,设置wirte_read_flag使共享内存段可写 shared->wirte_read_flag = 0; //输入了end,退出循环 if(strncmp(shared->text, "end", 3) == 0) running = 0; } else //有其他进程在写数据,不能读取数据 sleep(1); } //把共享内存从当前进程中分离 if(shmdt(shm) == -1) { fprintf(stderr, "shmdt failed\n"); exit(EXIT_FAILURE); } //删除共享内存 if(shmctl(shmid, IPC_RMID, 0) == -1) { fprintf(stderr, "shmctl(IPC_RMID) failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
-
- shmwrite进程,取得共享内存,将共享内存映射到自己的内存空间,向内存中写数据。
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/shm.h> struct shared_use_st { int wirte_read_flag; //作为一个标志,非0:表示可读,0表示可写 char text[1024]; //记录写入和读取的文本 }; int main() { int running = 1; void *shm = NULL; struct shared_use_st *shared = NULL; char buffer[1024 + 1]; //用于保存输入的文本 int shmid; //创建共享内存,当key一样时,返回的shmid也是一样的,则两个进程访问同一块共享内存,IPC_CREAT表示创建一块指定key的共享内存 shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT); if(shmid == -1) { fprintf(stderr, "shmget failed\n"); exit(EXIT_FAILURE); } //将共享内存连接到当前进程的地址空间 shm = shmat(shmid, (void*)0, 0); if(shm == (void*)-1) { fprintf(stderr, "shmat failed\n"); exit(EXIT_FAILURE); } printf("Memory attached at %X\n", (int)shm); //设置共享内存 shared = (struct shared_use_st*)shm; while(running) //向共享内存中写数据 { //数据还没有被读取,则等待数据被读取,不能向共享内存写 while(shared->wirte_read_flag == 1) { sleep(1); printf("Waiting...\n"); } //向共享内存中写入数据,向进程的地址空间shm里写就相当于往共享内存里写! printf("Enter some text: "); fgets(buffer, BUFSIZ, stdin); strncpy(shared->text, buffer, 1024); //写完数据,设置written使共享内存段可读 shared->wirte_read_flag = 1; //输入了end,退出循环 if(strncmp(buffer, "end", 3) == 0) running = 0; } //把共享内存从当前进程中分离 if(shmdt(shm) == -1) { fprintf(stderr, "shmdt failed\n"); exit(EXIT_FAILURE); } sleep(2); exit(EXIT_SUCCESS); }
(ps:以上程序转自http://blog.csdn.net/ljianhui/article/details/10253345)