Linux 支持两种方式的共享内存:System V 和 POSIX 共享内存。函数
System V 共享内存和共享文件映射的不足:ui
基于以上不足,POSIX.1b 定义了一组新的共享内存 API: POSIX 共享内存。code
POSIX 共享内存可以让无关进程共享一个映射区域而无需建立一个相应的映射文件。Linux 从内核 2.4 起开始支持 POSIX 共享内存。对象
不少类 UNIX 实现采用了文件系统来标识共享内存对象。一些 UNIX 实现将共享内存对象名建立为标准文件系统上一个特殊位置处的文件。Linux 使用挂载于 /dev/shm 目录下的专用 tmpfs 文件系统. 这个文件系统具备内核持久性--它所包含的共享内存对象会一直持久,即便当前不存在任何进程打开它,但这些对象会在系统关闭以后丢失.进程
系统上 POSIX 共享内存区域占据的内存总量受限于底层的 tmpfs 文件系统的大小。这个文件系统一般会在启动时使用默认大小(如 256 MB)进行挂载。若是有必要的话,超级用户可以经过使用命令 mount -o remount,size=
从新挂载这个文件系统来修改它的大小。 ip
使用 POSIX 共享内存对象的流程:内存
shm_open() 函数建立和打开一个新的共享内存对象或打开一个既有对象。传入 shm_open() 的参数与传入 open()的参数相似。rem
#include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ int shm_open(const chart *name, int oflag, mode_t mode); Returns file descriptor on success, or -1 on error. Link with -lrt.
name 参数标识出了待建立或待打开的共享内存对象。oflag 参数是一个改变调用行为的位掩码。字符串
oflag 参数的位值:string
在一个新共享内存对象被建立时,其全部权和组全部权将根据调用 shm_open() 的进程的有效用户和组 ID 来设定,对象权限将会根据 mode 参数中设置的掩码值来设定。mode 参数能取的位值与文件上的权限位值是同样的。与 open() 系统调用同样,mode 中的权限掩码将会根据进程的 umask 来取值。与 open() 不一样的是,在调用 shm_open() 时老是须要 mode 参数,在不建立新对象时须要将这个参数值指定为 0.
shm_open() 返回的文件描述符会设置 close-on-exec 标记,所以当程序执行了一个 exec() 时文件描述符会被自动关闭。
一个新共享内存对象被建立时其初始长度被会设置为 0。这意味着在建立完一个新共享内存对象以后一般在调用 mmap() 以前须要调用 ftruncate() 来设置对象的大小。在调用完 mmap() 以后可能还须要使用 ftuncate() 来根据需求扩大或收缩共享内存对象。
在扩展一个共享内存对象时,新增长的字节会自动被初始化为 0。
在任什么时候候均可以在 shm_open() 返回的文件描述符上使用 fstat() 以获取一个 stat 结构,该结构的字段会包含与这个共享内存对象相关的信息,包括其大小(st_size)、权限(st_mode)、全部者(st_uid)以及组(st_gid)。
使用 fchmod() 和 fchown() 可以分别修改共享内存对象的权限和全部权。
该程序建立了一个大小经过命令参数指定的共享内存对象并将该对象映射进进程的虚拟地址空间。
#include<stdio.h> #include<string.h> #include<fcntl.h> #include<sys/mman.h> #include<sys/stat.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int flags, opt, fd; mode_t perms; size_t size; void *addr; /* Create shared memory object and set its size */ fd = shm_open(argv[1], O_RDWR|O_CREAT, 0777); if (fd == -1) { printf("shm_open failed"); exit(-1); } if (ftruncate(fd, 10000) == -1) { printf("ftruncate failed"); exit(-1); } /* Map shared memory object */ addr = mmap(NULL, 10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { printf("mmap failed"); exit(-1); } exit(EXIT_SUCCESS); }
如上程序建立一各 10000 字节的共享内存对象:
# gcc pshm_create.c -o pshm_create -lrt # ./pshm_create demo_shm # ls -l /dev/shm/demo_shm -rwxr-xr-x 1 root root 10000 Jun 16 03:34 /dev/shm/demo_shm
将数据复制进一个 POSIX 共享内存对象.
#include<stdio.h> #include<string.h> #include<fcntl.h> #include<sys/mman.h> #include<sys/stat.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int fd; size_t len; char *addr; /* Open existing object */ fd = shm_open(argv[1], O_RDWR, 0); if (fd == -1) { printf("shm_open failed"); exit(-1); } len = strlen(argv[2]); /* Resize object to hold string */ if (ftruncate(fd, len) == -1) { printf("ftruncate failed"); exit(-1); } addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { printf("mmap failed"); exit(-1); } if (close(fd) == -1) { printf("close failed"); exit(-1); } printf("copying %ld bytes\n", (long)len); /* Copy string to shared memory */ memcpy(addr, argv[2], len); exit(EXIT_SUCCESS); }
向 1.3 建立的共享内存对象 demo_shm 写入数据:
# gcc pshm_write.c -o pshm_write -lrt # ls -l /dev/shm/demo_shm -rwxr-xr-x 1 root root 10000 Jun 16 03:34 /dev/shm/demo_shm # ./pshm_write demo_shm 'hello' copying 5 bytes # ls -l /dev/shm/demo_shm -rwxr-xr-x 1 root root 5 Jun 16 03:46 /dev/shm/demo_shm
从一个 POSIX 共享内存对象中复制数据。
#include<stdio.h> #include<string.h> #include<fcntl.h> #include<sys/mman.h> #include<sys/stat.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int fd; char *addr; struct stat sb; /* Open existing object */ fd = shm_open(argv[1], O_RDONLY, 0); if (fd == -1) { printf("shm_open failed"); exit(-1); } /* Use shared memory object size as length argument for mmap() * and as number of bytes to write() */ if (fstat(fd, &sb) == -1) { printf("fstat failed"); exit(-1); } addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { printf("mmap failed"); exit(-1); } if (close(fd) == -1) { printf("close failed"); exit(-1); } write(STDOUT_FILENO, addr, sb.st_size); printf("\n"); exit(EXIT_SUCCESS); }
显示刚才写到共享内存对象 demo_shm 中的字符串:
# gcc pshm_read.c -o pshm_read -lrt # ./pshm_read demo_shm hello
POSIX 共享内存对象具备内核持久性,即它们会持续存在直到被显示删除或系统重启。当再也不须要一个共享内存对象时就应该使用 shm_unlink() 删除它。
#include <sys/mman.h> int shm_unlink(const char *name); Returns 0 on success, or -1 on error Link with -lrt.
shm_unlink() 函数会删除经过 name 指定的共享内存对象。删除一个共享内存对象不会影响对象的既有映射(它会保持有效直到相应的进程调用 munmap() 或终止),但会阻止后续的 shm_open() 调用打开这个对象。一旦全部进程都接触映射这个对象,对象就会删除,其中的内容会丢失。
#include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { if (shm_unlink(argv[1]) == -1) { printf("shm_unlink failed"); exit(-1); } exit(EXIT_SUCCESS); }
删除上面建立的共享内存对象 demo_shm:
# gcc pshm_unlink.c -o pshm_unlink -lrt # ./pshm_unlink demo_shm