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