进程间的通信方式我们都熟悉,管道(命名管道)、信号(signal)、共享内存、消息队列、套接字(socket),关于信号量个人认为应该归为进程间的同步机制里。
下面我们就说说共享内存的通信方式。 它是IPC中最快的。一旦内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不在涉及内核。但是存取数据的时候需要保持同步. 关于共享内存权威的参考资料为《unix网络编程卷2》. 在前面我们讲解了mmap的内核机制,在posix v的共享内存的方式就是这个原理。system v也类似,只不过对接口进行了封装.
我们先从posix v的方式开始说起,它常用的函数接口:
open/shm_open + mmap 、munmap、msync、shm_unlink等.
既然是open那么就会对应文件,当然并不是所有的文件都可以映射.这里支持的文件类型:
1 .普通的磁盘文件
2. 设备文件(除去一些特殊的文件)
3.内存文件 (例如tmpfs)
4. 匿名映射 (主要用在具有亲缘关系的进程间)
先说一下常用的匿名映射,直接open /dev/zero 保证映射区域都初始化为0 的匿名映射。 还有可以直接用mmap来映射,而不用打开任何文件:
点击(此处)折叠或打开
- mmap( NULL,size_of_map,PROT_READ|PROT_WRITE,MAP_SHARED | MAP_ANON, -1,0 );
点击(此处)折叠或打开
- void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
下面说一下一般mmap方式的共享内存需要注意的:
映射的内存区域大小和文件大小,内存映射的大小都是页面的整数倍(PAGESIZE 4096默认) 比如文件大小为5000,映射了5000 ,那么可以访问2*PAGESIZE的内存区域,但是大于5000的内存写不会同步到文件里。这个内存可以访问的大小跟文件大小也是有关系的。映射的时候会自动检查底层的支持即文件的大小. 但是我们知道mmap的方式的优点就是可以动态的改变文件的大小,函数接口为ftruncate 和fstat. 具体用法参考unix网络编程卷2.
下面就简单总结下它的 特点:
1. 可以随时改变其大小
操作函数接口:ftruncate 和fstat
2. 必须先创建打开一个文件为基础 ,这个文件可以是磁盘文件 或者内存文件比如tmpfs下的文件;如果共享内存的配置需要写入到磁盘 ,可以选择打开磁盘文件。如果在嵌入式里用一般会把flash一个文件作为映射 ,既然需要频繁的操作文件 和磁盘读写 所以建议还是用shm。 毕竟flash频繁读写影响寿命和容易出现坏块。
函数接口: shmget、shmat、shmdt、shmctl等。
shm方式有些限制:
shmax 一个共享内存区的最大字节数 (具体系统不太一样)
shmmnb 一个共享内存区的最小字节数 1
shmmni 系统范围最大共享内存区标识数 128
shmseg 每个进程附接的最大共享内存区数 32
这些信息可以通过proc文件系统来查看。 cat /proc/sys/kernel/shmmax 等. 具体查看映射的内存区 可以通过ipcs命令来查看和操作.
我们也总结下它的特点:
1.不需要创建和打开文件
2. 如果是多进程间通信,需要统一 一个key标示 。动态生成的不一定一致。
3.读写的速度高于mmap的方式
4. 共享内存空间不能太大,毕竟直接占用内存空间.
不论哪种方式,都会涉及多进程间的通信、数据交互什么的,那么就必须保持互斥和同步,这里最常用的方式是采用信号量的方式.
信号量的接口:sem_init、sem_wait、sem_post. 由于用了信号量编译的时候会需要链接线程库 -lpthread.
下面就具体代码示例看看它们的具体用法:
posix v方式:
1. 初始化,之后进入循环不停的写,周期为1s。
点击(此处)折叠或打开
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <semaphore.h>
- #define CLUSTER_SHARED_FILE "/tmp/cluster_share"
- typedef struct{
- char name[4];
- int age;
- }people;
- struct stu {
- sem_t mutex;
- people s[10];
- };
- struct stu t;
- main(int argc, char** argv) // map a normal file as shared mem:
- {
- int fd,i;
- struct stu *p_map;
- char temp;
-
- fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR|O_TRUNC,00777);
- lseek(fd,sizeof(t)-1,SEEK_SET);
- write(fd,"",1);
-
- p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
- close(fd);
- temp = 'a';
- sem_init(&p_map->mutex,1,1);
- int j=0;
- while(1)
- {
- j++;
- printf("j 1\n");
- sem_wait(&p_map->mutex);
- printf("j 2\n");
- for(i=1; i<10; i++)
- {
- // temp += 1;
-
- memcpy(p_map->s[i].name, &temp,2 );
- p_map->s[i].age = p_map->s[i].age +20+i+j;
- }
- printf("j 3\n");
- sem_post(&p_map->mutex);
- printf("j 4\n");
- sleep(1);
- }
- printf(" initialize over \n ");
- sleep(50);
- munmap( p_map, sizeof(t) );
- printf( "umap ok \n" );
- }
点击(此处)折叠或打开
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/sem.h>
- #include <semaphore.h>
- #define CLUSTER_SHARED_FILE "/tmp/cluster_share"
- typedef struct{
- char name[4];
- int age;
- }people;
- struct stu {
- sem_t mutex;
- people s[10];
- };
- struct stu t;
- main(int argc, char** argv) // map a normal file as shared mem:
- {
- int fd,i;
- struct stu *p_map;
- char temp;
-
- fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR,00777);
- // lseek(fd,sizeof(people)*5-1,SEEK_SET);
- // write(fd,"",1);
-
- p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
- close( fd );
- while(1)
- {
- sleep(1);
- printf("2.....s\n");
- sem_wait(&p_map->mutex);
- printf("2.....\n");
- for(i=0; i<10; i++)
- {
- printf("name:%s,age:%d\n",p_map->s[i].name,p_map->s[i].age);
-
- }
- printf("2.....0\n");
- sem_post(&p_map->mutex);
- printf("2.......1");
- }
- munmap( p_map, sizeof(t));
- printf( "umap ok \n" );
- }
system V的方式,虽然接口不太一样,但是也很类似。
1. 初始化
点击(此处)折叠或打开
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/shm.h>
- #include <semaphore.h>
- //#include "shmdata.h"
- #define TEXT_SZ 2048
-
- struct shared_use_st
- {
- sem_t mutex;
- int written;//作为一个标志,非0:表示可读,0表示可写
- char text[TEXT_SZ];//记录写入和读取的文本
- int cnt;
- };
- int main()
- {
- int running = 1;
- void *shm = NULL;
- struct shared_use_st *shared = NULL;
- char buffer[4096]="hello world";
- int shmid;
- int i=0;
- //创建共享内存
- #if 0
- //删除共享内存
- if(shmctl(shmid, IPC_RMID, 0) == -1)
- {
- fprintf(stderr, "shmctl(IPC_RMID) failed\n");
- exit(EXIT_FAILURE);
- }
- #endif
- shmid = shmget((key_t)12345, 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;
- sem_init(&shared->mutex,1,1);
-
- while(running)//向共享内存中写数据
- {
- strncpy(shared->text, buffer, TEXT_SZ);
- sem_wait(&shared->mutex);
- //shared->cnt = 0;
- shared->cnt = shared->cnt +i;
- sem_post(&shared->mutex);
- i++;
- shared->written = 1;
- sleep(1);
- }
- //把共享内存从当前进程中分离
- if(shmdt(shm) == -1)
- {
- fprintf(stderr, "shmdt failed\n");
- exit(EXIT_FAILURE);
- }
- sleep(2);
- exit(EXIT_SUCCESS);
- }
点击(此处)折叠或打开
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/shm.h>
- //#include "shmdata.h"
- #include <semaphore.h>
- #define TEXT_SZ 2048
-
- struct shared_use_st
- {
- sem_t mutex;
- int written;//作为一个标志,非0:表示可读,0表示可写
- char text[TEXT_SZ];//记录写入和读取的文本
- int cnt;
- };
- int main()
- {
- int running = 1;//程序是否继续运行的标志
- void *shm = NULL;//分配的共享内存的原始首地址
- struct shared_use_st *shared;//指向shm
- int shmid;//共享内存标识符
- //创建共享内存
- shmid = shmget((key_t)12345, sizeof(struct shared_use_st), 0666);
- 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->written = 0;
- while(running)//读取共享内存中的数据
- {
- sem_wait(&shared->mutex);
- printf("text:%s,cnt:%d\n",shared->text,shared->cnt);
- sem_post(&shared->mutex);
- sleep(1);
- }
- //把共享内存从当前进程中分离
- if(shmdt(shm) == -1)
- {
- fprintf(stderr, "shmdt failed\n");
- exit(EXIT_FAILURE);
- }
- #if 0
- //删除共享内存
- if(shmctl(shmid, IPC_RMID, 0) == -1)
- {
- fprintf(stderr, "shmctl(IPC_RMID) failed\n");
- exit(EXIT_FAILURE);
- }
- #endif
- exit(EXIT_SUCCESS);
- }
整体的用法还算比较简单. 也很高效方便.