1、概述
系統調用mmap通過映射一個普通文件實現共享內存。System V 則是通過映射特殊文件系統shm中的文件實現進程間的共享內存通信。也就是說,每個共享內存區域對應特殊文件系統shm中的一個文件。執行過程是先調用shmget,再調用shmat。對於每個共享的內存區,內核維護如下的信息結構,定義在<sys/shm.h>頭文件中。
1 struct shmid_ds { 2 struct ipc_perm shm_perm; /* operation perms */ 3 int shm_segsz; /* size of segment (bytes) */ 4 time_t shm_atime; /* last attach time */ 5 time_t shm_dtime; /* last detach time */ 6 time_t shm_ctime; /* last change time */ 7 unsigned short shm_cpid; /* pid of creator */ 8 unsigned short shm_lpid; /* pid of last operator */ 9 short shm_nattch; /* no. of current attaches */ 10 /* the following are private */ 11 unsigned short shm_npages; /* size of segment (pages) */ 12 unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */ 13 struct vm_area_struct *attaches; /* descriptors for attaches */ 14 };
參考網址:http://www.tldp.org/LDP/lpg/node68.html
2、System V 共享內存區API
使用共享內存的流程:
1.進程必須首先分配它。
2.隨后需要訪問這個共享內存塊的每一個進程都必須將這個共享內存綁定到自己的地址空間中。
3.當完成通信之后,所有進程都將脫離共享內存,並且由一個進程釋放該共享內存塊。
#include <sys/ipc.h> #include <sys/shm.h> /* 創建一個新的內存共享區或者訪問一個已經存在的共享內存區 返回共享內存區標識符 */ int shmget(key_t key, size_t size, int shmflg); /* 創建或打開一個共享內存區后,調用shmat把它連接到調用進程的地址空間 */ void *shmat(int shmid, const void *shmaddr,int shmflg); /* 當一個進程完成某個共享內存區的使用時,調用shmdt斷開這個內存區 */ int shmdt(const void *shmaddr); /* 對內存區進行多種操作 cmd取值: IPC_RMID:從系統中刪除由shmid標識的共享內存區並拆除它 IPC_SET:給指定的共享內存區設置其shmid_ds結果成員 IPC_STAT:通過buff參數向調用者返回所指定共享內存區當前的shmid_ds結構 */ int shmctl(int shmid, int cmd, struct shmid_ds *buf);
調用System V API編寫程序進行測試:
程序1:調用shmget函數使用指定的路徑名和長度創建一個共享內存區,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/shm.h> 5 #include <fcntl.h> 6 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6) 8 9 int main(int argc,char *argv[]) 10 { 11 int c,id,oflag; 12 char *ptr; 13 size_t length; 14 oflag = SVSHM_MODE | IPC_CREAT; 15 while(( c = getopt(argc,argv,"e")) != -1) 16 { 17 switch(c) 18 { 19 case 'e': 20 oflag |= O_EXCL; 21 break; 22 } 23 } 24 if (optind != argc -2) 25 { 26 printf("usage: shmget [-e] <pathname> <length>.\n"); 27 exit(0); 28 } 29 length = atoi(argv[optind + 1]); 30 //創建由用戶指定其名字和大小的共享內存區 31 id = shmget(ftok(argv[optind],0),length,oflag); 32 //把該內存區連接到當前進程的地址空間 33 ptr = shmat(id,NULL,0); 34 exit(0); 35 }
程序2:調用shmctl指定IPC_RMID命令,從系統中刪除一個共享內存區,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/shm.h> 5 6 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6) 7 8 int main(int argc,char* argv[]) 9 { 10 int id; 11 if(argc != 2) 12 { 13 printf("usage: shmrmid <pathname>\n"); 14 exit(0); 15 } 16 //打開共享內存區 17 id = shmget(ftok(argv[1],0),0,SVSHM_MODE); 18 //從系統中刪除由id標識的共享內存區 19 shmctl(id,IPC_RMID,NULL); 20 exit(0); 21 }
程序3:往共享內存區中寫入一個模式,調用shmctl指定IPC_STAT命令格式,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/shm.h> 5 #include <fcntl.h> 6 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6) 8 9 int main(int argc,char *argv[]) 10 { 11 int i,id; 12 struct shmid_ds buff; 13 unsigned char *ptr; 14 if(argc != 2) 15 { 16 printf("usage: shmwrite <pathname>.\n"); 17 exit(0); 18 } 19 id = shmget(ftok(argv[1],0),0,SVSHM_MODE); 20 ptr = shmat(id,NULL,0); 21 shmctl(id,IPC_STAT,&buff); //獲取共享內存區大小 22 for(i=0;i<buff.shm_segsz;i++) 23 *ptr++ = i % 256; 24 exit(0); 25 }
程序4:從共享內存去中讀出模式,程序如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/shm.h> 5 #include <fcntl.h> 6 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6) 8 9 int main(int argc,char *argv[]) 10 { 11 int i,id; 12 struct shmid_ds buff; 13 unsigned char c,*ptr; 14 if(argc != 2) 15 { 16 printf("usage: shmread <pathname>.\n"); 17 exit(0); 18 } 19 id = shmget(ftok(argv[1],0),0,SVSHM_MODE); 20 ptr = shmat(id,NULL,0); 21 shmctl(id,IPC_STAT,&buff); 22 for(i=0;i<buff.shm_segsz;i++) 23 { 24 c = *ptr++; 25 printf("ptr[%d] = %d\n",i,c); 26 } 27 exit(0); 28 }
3、System V 與Posix 共享內存區
二者的差別是:
(1)Posix共享內存區是先調用shm_open然后再調用mmap,System V 共享內存區是先調用shmget再調用shmat。
(2)Posix共享內存區對象的大小可在任何時刻通過ftruncate修改,而System V 共享內存區對象的大小是在調用shmget創建時固定下來的。