1、概述
Posix提供了两种在无亲缘关系进程间共享内存区的方法:
(1)内存映射文件:先有open函数打开,然后调用mmap函数把得到的描述符映射到当前进程地址空间中的一个文件(上一篇笔记所用到的就是)。
(2)共享内存区对象:先有shm_open打开一个Posix IPC名字(也可以是文件系统中的一个路径名),然后调用mmap将返回的描述符映射到当前进程的地址空间。
者两种方法多需要调用mmap,差别在于作为mmap的参数之一的描述符的获取手段。
2、Posix共享内存区对象
Posix共享内存区涉及以下两个步骤要求:
(1)指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或打开一个已经存在的共享内存区对象。
(2)调用mmap把这个共享内存区映射到调用进程的地址空间。
注意:mmap用于把一个内存区对象映射到调用进程地址空间的是该对象的一个已经打开描述符。
关于Posix共享内存区对象的API如下:
1 #include <sys/mman.h> 2 #include <sys/stat.h> /* For mode constants */ 3 #include <fcntl.h> /* For O_* constants */ 4 int shm_open(const char *name, int oflag, mode_t mode); 5 int shm_unlink(const char *name); 6 int ftruncate(int fd, off_t length); 7 int fstat(int fd, struct stat *buf);
参考网址 http://linux.die.net/man/3/shm_open http://linux.die.net/man/2/ftruncate
简单的程序:
(1)指定名字和长度创建一个共享内存区对象,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 #include <sys/mman.h> 6 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 7 int main(int argc,char* argv[]) 8 { 9 int c,fd,flags; 10 char *ptr; 11 off_t length; 12 flags = O_RDWR | O_CREAT; 13 while( (c = getopt(argc,argv,"e")) != -1) 14 { 15 switch(c) 16 { 17 case 'e': 18 flags |= O_EXCL; 19 break; 20 } 21 } 22 if(optind != argc -2) 23 { 24 perror("usage: shmcreate [-e] <name> <length>"); 25 exit(0); 26 } 27 length = atoi(argv[optind + 1]); 28 fd = shm_open(argv[optind],flags,FILE_MODE); 29 ftruncate(fd,length); 30 ptr = mmap(NULL,length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd,0); 31 exit(0); 32 }
(2)从系统中删除一个共享内存区对象的名字,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 #include <sys/mman.h> 6 #include <errno.h> 7 int main(int argc,char* argv[]) 8 { 9 if(argc != 2) 10 { 11 printf("usage: posixshmunlink <name>"); 12 exit(0); 13 } 14 if(shm_unlink(argv[1]) == -1) 15 { 16 perror("shm_unlink error"); 17 exit(-1); 18 } 19 exit(0); 20 }
(3)往共享内存区对象中写入一个模式,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 #include <sys/mman.h> 6 #include <errno.h> 7 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 8 int main(int argc,char *argv[]) 9 { 10 int i,fd; 11 struct stat stat; 12 unsigned char *ptr; 13 if(argc != 2) 14 { 15 printf("usage: shmwrite <name>"); 16 exit(0); 17 } 18 fd = shm_open(argv[1],O_RDWR,FILE_MODE); //打开所指定的共享内存区对象 19 fstat(fd,&stat); //获取内存区的大小 20 ptr = mmap(NULL,stat.st_size,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0); //映射到共享内存区 21 close(fd); 22 for(i=0;i<stat.st_size;++i) 23 *ptr++ = i % 256; 24 exit(0); 25 }
3、例子
采用Posix共享内存实现给一个共享的计数器持续加1,它由多个进程给存放在共享内存区中的某个计数器持续加1。将计数器放在一个共享内存区中,并用一个有名信号量来同步。
创建并初始化共享内存区和信号量的程序如下:server1.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/mman.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <fcntl.h> 8 #include <errno.h> 9 #include <semaphore.h> 10 11 #define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 12 13 //计数器结构体 14 struct shmstruct 15 { 16 int count; 17 }; 18 //同步有名信号量 19 sem_t *mutex; 20 21 int main(int argc,char *argv[]) 22 { 23 int fd; 24 struct shmstruct *ptr; 25 if(argc != 3) 26 { 27 printf("usage: server1 <shmname> <semname>.\n"); 28 exit(0); 29 } 30 //防止所需共享内存区对象已经存在 31 shm_unlink(argv[1]); 32 //创建一个新的共享内存区对象 33 if((fd = shm_open(argv[1],O_RDWR | O_CREAT | O_EXCL,FILE_MODE)) == -1) 34 { 35 perror("shm_open error"); 36 exit(-1); 37 } 38 //指定新创建的共享内存区对象的大小 39 ftruncate(fd,sizeof( struct shmstruct)); 40 //将新创建的共享内存区映射到调用进程的地址空间 41 if((ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED) 42 { 43 perror("mmap error"); 44 exit(-1); 45 } 46 //关闭对象描述符 47 close(fd); 48 //防止所需的信号量已经存在 49 sem_unlink(argv[2]); 50 //创建有名信号量,作为互斥锁用 51 if((mutex = sem_open(argv[2],O_CREAT|O_EXCL,FILE_MODE,1)) == SEM_FAILED) 52 { 53 perror("sem_open error"); 54 exit(-1); 55 } 56 //关闭信号量 57 sem_close(mutex); 58 exit(0); 59 }
给存放在共享内存区中的一个计数器加1程序如下:client1.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/mman.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <fcntl.h> 8 #include <errno.h> 9 #include <semaphore.h> 10 11 #define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 12 13 struct shmstruct 14 { 15 int count; 16 }; 17 18 sem_t *mutex; 19 20 int main(int argc,char *argv[]) 21 { 22 int fd,i,nloop; 23 pid_t pid; 24 struct shmstruct *ptr; 25 if(argc != 4) 26 { 27 printf("usage: client1 <shmname> <semname> <#loops>.\n"); 28 exit(0); 29 } 30 nloop = atoi(argv[3]); 31 //打开共享内存区 32 if((fd = shm_open(argv[1],O_RDWR,FILE_MODE)) == -1) 33 { 34 perror("shm_open error"); 35 exit(0); 36 } 37 //将共享内存区映射到进程地址空间 38 if((ptr = mmap(NULL,sizeof(struct shmstruct),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED) 39 { 40 perror("mmap error"); 41 exit(-1); 42 } 43 close(fd); 44 //打开信号量 45 if((mutex = sem_open(argv[2],0)) == SEM_FAILED) 46 { 47 printf("sem_open error"); 48 exit(-1); 49 } 50 pid = getpid(); 51 for(i=0;i<nloop;i++) 52 { 53 sem_wait(mutex); //锁住信号量 54 printf("pid %ld: %d\n",(long) pid,ptr->count++); 55 sem_post(mutex); //释放信号量 56 } 57 exit(0); 58 }
程序执行结果如下所示: