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 }
程序執行結果如下所示: