Linux的內核支持多種共享內存方式,如mmap()系統調用,Posix共享內存,以及System V共享內存。node
mmap()系統調用使得進程之間經過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程能夠像訪問普通內存同樣對文件進行訪問,沒必要再調用read(),write()等操做。數組
注:實際上,mmap()系統調用並非徹底爲了用於共享內存而設計的。它自己提供了不一樣於通常對普通文件的訪問方式,進程能夠像讀寫內存同樣對普通文件的操做。而Posix或System V的共享內存IPC則純粹用於共享目的,固然mmap()實現共享內存也是其主要應用之一。數據結構
#include <sys/mman.h> void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
同malloc函數申請內存空間相似的,mmap創建的映射區在使用結束後也應調用相似free的函數來釋放。函數
int munmap(void *addr, size_t length); //成功:0; 失敗:-1
eg.設計
// 寫共享內存 #include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> typedef struct { char name[32]; int age; } people; int main(int argc, char** argv) { if(argc < 2) { fprintf(stderr,"Usage: %s filename \n", argv[0]); return -1; } people* p_map; char temp = 'a'; int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 00777); if (-1 == fd) { printf("open file error = %s\n", strerror(errno)); return -1; } ftruncate(fd, sizeof(people)*10); p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == p_map) { printf("mmap file error = %s\n", strerror(errno)); return -1; } for(int i = 0; i < 10; i++) { memcpy( (*(p_map+i)).name, &temp, 1); (*(p_map+i)).name[1] = 0; (*(p_map+i)).age = 20+i; temp += 1; } printf("initialize over\n"); close(fd); munmap(p_map, sizeof(people)*10); printf("umap ok \n"); return 0; } // 讀共享內存 #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/stat.h> typedef struct { char name[32]; int age; } people; int main(int argc, char** argv) { if(argc < 2) { fprintf(stderr,"Usage: %s filename \n", argv[0]); return -1; } people* p_map; struct stat filestat; int fd = open(argv[1], O_CREAT|O_RDWR, 00777); if (-1 == fd) { printf("open file error = %s\n", strerror(errno)); return -1; } fstat(fd, &filestat); p_map = (people*)mmap(NULL, filestat.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == p_map) { printf("mmap file error = %s\n", strerror(errno)); return -1; } for(int i = 0; i < 10; i++) { printf("name = %s, age = %d\n",(*(p_map+i)).name, (*(p_map+i)).age); } close(fd); munmap(p_map, sizeof(people)*10); printf("umap ok \n"); return 0; }
運行結果:3d
執行./mmap_w people.txt指針
執行./mmap_r people.txtcode
POSIX共享內存使用方法有如下兩個步驟:對象
shm_open
建立或打開一個POSIX共享內存對象mmap
將它映射到當前進程的地址空間和經過內存映射文件進行通訊的使用上差異在於mmap描述符參數獲取方式不同:經過open或shm_open。POSIX共享內存和POSIX消息隊列,有名信號量同樣都是具備隨內核持續性的特色。blog
#include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ // 打開一個共享內存的文件句柄 int shm_open(const char *name, int oflag, mode_t mode); //注意 這裏的名字具備形式 /somename,即必須以 / 爲開頭,由於POSIX共享內存對應的文件是位於/dev/shm這個特殊的文件系統內。 // 刪除一個共享內存的名字,但只有全部程序都關閉,纔會真的刪除 int shm_unlink(const char *name);
eg.
寫共享內存
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> typedef struct { char name[32]; int age; } people; main(int argc, char** argv) { if(argc < 2) { fprintf(stderr,"Usage: %s /filename \n", argv[0]); return -1; } people* p_map; char temp = 'a'; int fd = shm_open(argv[1], O_CREAT|O_RDWR, 00777); if (-1 == fd) { printf("open file error = %s\n", strerror(errno)); return -1; } ftruncate(fd, sizeof(people)*10); p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == p_map) { printf("mmap file error = %s\n", strerror(errno)); return -1; } for(int i = 0; i < 10; i++) { memcpy( ( *(p_map+i) ).name, &temp, 1); ( *(p_map+i) ).name[1] = 0; ( *(p_map+i) ).age = 20+i; temp += 1; } printf("initialize over\n"); close(fd); munmap(p_map, sizeof(people)*10); printf("umap ok \n"); return 0; }
讀共享內存
#include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/stat.h> typedef struct { char name[32]; int age; } people; main(int argc, char** argv) { if(argc < 2) { fprintf(stderr,"Usage: %s /filename \n", argv[0]); return -1; } people* p_map; struct stat filestat; int fd = shm_open(argv[1], O_CREAT|O_RDWR, 00777); if (-1 == fd) { printf("open file error = %s\n", strerror(errno)); return -1; } fstat(fd, &filestat); p_map = (people*)mmap(NULL, filestat.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == p_map) { printf("mmap file error = %s\n", strerror(errno)); return -1; } for(int i = 0; i < 10; i++) { printf("name = %s, age = %d\n",(*(p_map+i)).name, (*(p_map+i)).age); } close(fd); munmap(p_map, sizeof(people)*10); printf("umap ok \n"); return 0; }
刪除共享內存
#include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/stat.h> main(int argc, char** argv) { if(argc < 2) { fprintf(stderr,"Usage: %s /filename \n", argv[0]); return -1; } int ret = shm_unlink(argv[1]); if (-1 == ret) { printf("unlink shm error = %s\n", strerror(errno)); return -1; } printf("unlink ok \n"); return 0; }
運行結果:
[root@rocket ipc]# g++ -g -o ipc_posix_mmap_writer ipc_posix_mmap_writer.cpp -lrt [root@rocket ipc]# ./ipc_posix_mmap_writer /shm_from_mem.txt initialize over umap ok [root@rocket ipc]# g++ -g -o ipc_posix_mmap_reader ipc_posix_mmap_reader.cpp -lrt [root@rocket ipc]# ./ipc_posix_mmap_reader /shm_from_mem.txt name = a, age = 20 name = b, age = 21 name = c, age = 22 name = d, age = 23 name = e, age = 24 name = f, age = 25 name = g, age = 26 name = h, age = 27 name = i, age = 28 name = j, age = 29 umap ok [root@rocket ipc]# ./ipc_posix_mmap_unlink /shm_from_mem.txt unlink ok [root@rocket ipc]# ./ipc_posix_mmap_unlink /shm_from_mem.txt unlink shm error = No such file or directory [root@rocket ipc]# ll /dev/shm/|grep mem [root@rocket ipc]#
系統調用mmap()經過映射一個普通文件實現共享內存。System V則是經過映射特殊文件系統shm中的文件實現進程間的共享內存通訊。也就是說,每一個共享內存區域對應特殊文件系統shm中的一個文件(這是經過shmid_kernel結構聯繫起來的)。進程間須要共享的數據被放在一個叫作IPC共享內存區域的地方,全部須要訪問該共享區域的進程都要把該共享區域映射到本進程的地址空間中去。System V共享內存經過shmget得到或建立一個IPC共享內存區域,並返回相應的標識符。內核在保證shmget得到或建立一個共享內存區,初始化該共享內存區相應的shmid_kernel結構注同時,還將在特殊文件系統shm中,建立並打開一個同名文件,並在內存中創建起該文件的相應dentry及inode結構,新打開的文件不屬於任何一個進程(任何進程均可以訪問該共享內存區)。全部這一切都是系統調用shmget完成的。
每個共享內存區都有一個控制結構struct shmid_kernel,shmid_kernel是共享內存區域中很是重要的一個數據結構,它是存儲管理和文件系統結合起來的橋樑,定義以下:
struct shmid_kernel /* private to the kernel */ { struct kern_ipc_perm shm_perm; struct file* shm_file; int id; unsigned long shm_nattch; unsigned long shm_segsz; time_t shm_atim; time_t shm_dtim; time_t shm_ctim; pid_t shm_cprid; pid_t shm_lprid; };
該結構中最重要的一個域應該是shm_file,它存儲了將被映射文件的地址。每一個共享內存區對象都對應特殊文件系統shm中的一個文件,通常狀況下,特殊文件系統shm中的文件是不能用read()、write()等方法訪問的,當採起共享內存的方式把其中的文件映射到進程地址空間後,可直接採用訪問內存的方式對其訪問。
內核經過數據結構struct ipc_ids shm_ids維護系統中的全部共享內存區域。上圖中的shm_ids.entries變量指向一個ipc_id結構數組,而每一個ipc_id結構數組中有個指向kern_ipc_perm結構的指針。到這裏讀者應該很熟悉了,對於系統V共享內存區來講,kern_ipc_perm的宿主是shmid_kernel結構,shmid_kernel是用來描述一個共享內存區域的,這樣內核就可以控制系統中全部的共享區域。同時,在shmid_kernel結構的file類型指針shm_file指向文件系統shm中相應的文件,這樣,共享內存區域就與shm文件系統中的文件對應起來。
在建立了一個共享內存區域後,還要將它映射到進程地址空間,系統調用shmat()完成此項功能。因爲在調用shmget()時,已經建立了文件系統shm中的一個同名文件與共享內存區域相對應,所以,調用shmat()的過程至關於映射文件系統shm中的同名文件過程,原理與mmap()大同小異。
#include <sys/ipc.h> #include <sys/shm.h> // 獲取共享內存區域 int shmget(key_t key, size_t size, int shmflg); // 鏈接共享內存區域 void *shmat(int shmid, const void *shmaddr, int shmflg); // 斷開共享內存區域 int shmdt(const void *shmaddr); // 對共享內存區域進行控制 int shmctl(int shmid, int cmd, struct shmid_ds *buf); // 將path和proj_id轉換成System V IPC key key_t ftok(const char *pathname, int proj_id);
eg.
寫共享內存
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> typedef struct { char name[32]; int age; } people; int main(int argc, char** argv) { int shm_id,i; key_t key; people* p_map; char temp = 'a'; const char* name = "/dev/shm/my_systemv_shm1"; key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } shm_id=shmget(key, 4096, IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return -1; } p_map=(people*)shmat(shm_id,NULL,0); for(int i = 0; i < 10; i++) { memcpy( ( *(p_map+i) ).name, &temp, 1); ( *(p_map+i) ).name[1] = 0; ( *(p_map+i) ).age = 20+i; temp += 1; } printf("initialize over\n"); if(shmdt(p_map) == -1) { perror(" detach error "); return -1; } return 0; }
讀共享內存
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> typedef struct { char name[32]; int age; } people; int main(int argc, char** argv) { int shm_id,i; key_t key; people* p_map; const char* name = "/dev/shm/my_systemv_shm1"; key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } shm_id=shmget(key, 4096, IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return -1; } p_map=(people*)shmat(shm_id,NULL,0); for(int i = 0; i < 10; i++) { printf( "name:%s, ",(*(p_map+i)).name ); printf( "age %d\n",(*(p_map+i)).age ); } if(shmdt(p_map) == -1) { perror(" detach error "); return -1; } return 0; }
運行結果:
[root@rocket ipc]# g++ -g -o ipc_systemv_mmap_writer ipc_systemv_mmap_writer.cpp [root@rocket ipc]# touch /dev/shm/my_systemv_shm1 [root@rocket ipc]# ./ipc_systemv_mmap_writer initialize over [root@rocket ipc]# g++ -g -o ipc_systemv_mmap_reader ipc_systemv_mmap_reader.cpp [root@rocket ipc]# ./ipc_systemv_mmap_reader name:a, age 20 name:b, age 21 name:c, age 22 name:d, age 23 name:e, age 24 name:f, age 25 name:g, age 26 name:h, age 27 name:i, age 28 name:j, age 29 觀察一下共享內存: [root@rocket ipc]# ./get_ipc_key /dev/shm/my_systemv_shm1 key = 1084739 [root@rocket ipc]# ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 gdm 600 393216 2 dest 0x00000000 32769 gdm 600 393216 2 dest 0x00000000 65538 gdm 600 393216 2 dest 0x00000000 98307 gdm 600 393216 2 dest 0x00108d43 131076 root 0 4096 0 看到咱們新建的共享內存了吧?刪除也很簡單: [root@rocket ipc]# ipcrm -m 131076 [root@rocket ipc]# ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 gdm 600 393216 2 dest 0x00000000 32769 gdm 600 393216 2 dest 0x00000000 65538 gdm 600 393216 2 dest 0x00000000 98307 gdm 600 393216 2 dest