linux进程间通信之Posix共享内存用法详解及代码举例
Posix共享内存有两种非亲缘进程间的共享内存方法:
1). 使用内存映射文件,由open函数打开,再由mmap函数把返回的文件描述符映射到当前进程空间中的一个文件。
2). 使用共享内存区对象,由shm_open打开一个 Posix IPC名字。再由mmap把返回的描述符映射到当前进程的地址空间。
Posix共享内存相关函数头文件及原型:
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
功能:打开一个共享内存对象,获取描述符
返回值:成功返回非负描述符,出错返回-1
参数:name是Posix IPC名字;oflag参数为O_RDONLY或O_RDWR,还可以为O_CREAT,O_EXCL,O_TRUNC,当在共享内存对象已存在的情况下指定O_RDWR和O_TRUNC会被截短为长度0;mode是指定权限位,在有O_CREAT指定的情况下使用,没有O_CREAT时也要必须设为0。
int shm_unlink(const char *name);
功能:删除一个共享内存区对象的名字。
返回值: 成功返回0,出错返回-1.
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
功能:把由shm_open打开的共享内存对象映射到进程地址空间。
返回值:若成功则返回映射区的起始地址,出错返回MAP_FAILED。
参数: addr指定被映射到的进程空间的起始地址,一般设为空指针NULL,表示自动分配;len是映射到地址空间的字节数,从文件头开始第offset个字节开始算,一般offset设置为0;prot可以为PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问);flags可以为MAP_SHARED,MAP_PRIVATE,MAP_FIXED,其中MAP_SHARED或MAP_PRIVATE必须指定一个,MAP_FIXED一般不指定并且addr为空指针,表示自动分配地址,方便移植。
int munmap(void *addr, size_t len);
功能:删除进程地址空间的映射关系
返回值:成功返回0,出错返回-1
int msync(void *addr, size_t len, int flags);
功能:是文件内容与内存映射区中内容一致。
返回值: 成功返回0,出错返回-1.
参数: flags有3种,MS_ASYNC(异步写),MS_SYNC(同步写),MS_INVALIDATE(使缓存的数据失效)。
代码举例shm_test.c,父子进程操作共享内存区变量计数加1,并且用信号量sem做同步
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/mman.h>
- #include <semaphore.h>
- #include <fcntl.h>
- #define MYFILE "./shm_test_file"
- #define SEM_NAME "/mysem"
- #define MYSHM "/myshm"
- #define TIMES 5
- #define USE_SHM_OPEN 0
- #define SEM_IN_SHAREMEMORY 0
- #if SEM_IN_SHAREMEMORY
- struct shared
- {
- sem_t mutex;
- int count;
- }shared;
- #endif
- int main(void)
- {
- int fd,i,zero=0;
- #if SEM_IN_SHAREMEMORY
- struct shared *ptr;
- #if USE_SHM_OPEN
- fd=shm_open(MYSHM,O_RDWR|O_CREAT,0666);
- #else
- fd=open(MYFILE,O_RDWR|O_CREAT,0666);
- #endif
- write(fd,&shared,sizeof(struct shared));
- ptr=mmap(NULL,sizeof(struct shared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
- sem_init(&ptr->mutex,1,1);
- #else
- int *ptr;
- sem_t *mutex;
- #if USE_SHM_OPEN
- fd=shm_open(MYSHM,O_RDWR|O_CREAT,0666);
- #else
- fd=open(MYFILE,O_RDWR|O_CREAT,0666);
- #endif
- write(fd,&zero,sizeof(int));
- ptr=mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
- mutex=sem_open(SEM_NAME,O_CREAT|O_EXCL,0666,1);
- sem_unlink(SEM_NAME);
- #endif
- close(fd);
- setbuf(stdout,NULL);//stdout is unbuffered
- if(0==fork())
- {
- for(i=0;i<TIMES;i++)
- {
- #if SEM_IN_SHAREMEMORY
- sem_wait(&ptr->mutex);
- printf("child : %d\n",ptr->count++);
- sem_post(&ptr->mutex);
- #else
- sem_wait(mutex);
- printf("child : %d\n",(*ptr)++);
- sem_post(mutex);
- #endif
- }
- exit(0);
- }
- for(i=0;i<TIMES;i++)
- {
- #if SEM_IN_SHAREMEMORY
- sem_wait(&ptr->mutex);
- printf("parent : %d\n",ptr->count++);
- sem_post(&ptr->mutex);
- #else
- sem_wait(mutex);
- printf("parent : %d\n",(*ptr)++);
- sem_post(mutex);
- #endif
- }
- exit(0);
- }
运行结果:
./a.out
parent : 0
parent : 1
parent : 2
parent : 3
parent : 4
child : 5
child : 6
child : 7
child : 8
child : 9
通过设置“#define USE_SHM_OPEN 1”使用共享内存对象映射,“#define USE_SHM_OPEN 0” 使用不同文件系统映射。
设置“#define SEM_IN_SHAREMEMORY 1”使信号量SEM也放在共享内存区,“#define SEM_IN_SHAREMEMORY 0”使信号量SEM不在共享内存区