POSIX共享内存
3.3 POSIX共享内存
在Linux中,POSIX共享内存对象驻留在tmpfs伪文件系统中。系统默认挂载在/dev/shm目录下。当调用shm_open函数创建或打开POSIX共享内存对象时,系统会将创建/打开的共享内存文件放到/dev/shm目录下。
创建共享内存的基本步骤是:
-
程序执行shm_open函数创建了共享内存区域,此时会在/dev/shm/创建共享内存文件.
int shm_open(const char *name, int oflag, mode_t mode); /* 创建或打开一个共享内存,成功返回一个整数的文件描述符,错误返回-1。 1.name:共享内存区的名字; 2.oflag标志位: open的标志一样,一般填写O_CREAT|O_TRUNC|O_RDWR。 3.mode权限位: open的mode一样 */
-
通过ftruncate函数改变shm_open创建共享内存的大小为页大小sysconf(_SC_PAGE_SIZE)整数倍,如果不执ftruncate函数的话,会报Bus error的错误。
int ftruncate(int fd, off_t length); /*函数说明:ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。如果原来的文件大小比参数length大,则超过的部分会被删去。 返 回 值:0、-1 错误原因:errno EBADF 参数fd文件描述词为无效的或该文件已关闭 EINVAL 参数fd为一socket并非文件,或是该文件并非以写入模式打开 */
-
通过mmap函数将创建的共享内存文件映射到内存。
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset); /*函数说明:mmap()必须以PAGE_SIZE为单位进行映射。 start: 映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。 length: 映射区的长度,长度单位是以字节为单位,不足一内存页按一内存页处理 prot: 期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过或运算合理地组合在一起 PROT_EXEC 页内容可以被执行 PROT_READ 页内容可以被读取 PROT_WRITE 页可以被写入 PROT_NONE 页不可访问 flags: 指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体 MAP_SHARED 与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。 直到msync()或者munmap()被调用,文件实际上不会被更新。 MAP_PRIVATE 建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以 上标志是互斥的,只能使用其中一个。 MAP_LOCKED 锁定映射区的页面,从而防止页面被交换出内存。 fd: 有效的文件描述符。 offset: 被映射对象内容的起点。 成功返回共享内存地址,失败返回MAP_FAILED */
通过munmap卸载共享内存
int munmap(void* start,size_t length); /* * start:共享内存地址 * length:共享内存大小 */
-
通过shm_unlink删除内存共享文件
int shm_unlink(const char *name); /* *name:内存共享文件 */
我们用下面的源程序对POSIX共享内存进行测试,如下shmen_write.c:
void error_print(char *msg) { perror(msg); exit(-1); } int main (int argc, char *argv[]) { int ret, i; const char *memname = "/mymem"; size_t mem_size = sysconf(_SC_PAGE_SIZE); int fd = shm_open(memname, O_CREAT|O_TRUNC|O_RDWR, 0666); if (fd == -1) error_print("shm_open"); ret = ftruncate(fd, mem_size); if (ret != 0) error_print("ftruncate"); void *ptr = mmap(0, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) error_print("MMAP"); close(fd); for(i = 0; i < 20; i++) { sprintf((char*)ptr, "data %d", i); printf("write data %s\n", (char*)ptr); } ret = munmap(ptr, mem_size); if (ret != 0) error_print("munmap"); ret = shm_unlink(memname); if (ret != 0) error_print("shm_unlink"); return 0; }
编译程序shmen_write.c
gcc shmen_write.c -o shmen_write -lrt
shmen_read.c
void error_print(char *msg) { perror(msg); exit(-1); } int main (int argc, char *argv[]) { int ret, i; const char *memname = "/mymem"; //共享内存文件以/开头,可以更好兼容不同系统 struct stat statbuf; size_t mem_size = 2 * sysconf(_SC_PAGE_SIZE); int fd = shm_open(memname, O_CREAT|O_TRUNC|O_RDWR, 0666); if (fd == -1) error_print("shm_open"); ret = ftruncate(fd, mem_size); if (ret != 0) error_print("ftruncate"); void *ptr = mmap(0, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) error_print("MMAP"); close(fd); for(i = 0; i < 20; i++) { printf("read data %s\n", (char*)ptr); sleep(1); } ret = munmap(ptr, mem_size); if (ret != 0) error_print("munmap"); ret = shm_unlink(memname); if (ret != 0) error_print("shm_unlink"); return 0; }
编译程序shmen_read.c
gcc shmen_read.c -o shmen_read -lrt
先运行shmem_write,再运行shmem_read.