Posix 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。
Posix提供了两种在无亲缘关系进程间共享内存区的方法,内存映射文件和共享内存区对象,这两种共享内存的区别在于共享的数据的载体(底层支撑对象)不一样:
ps:经常说的Posix共享内存,一般是指共享内存区对象,也就是共享物理内存
Posix共享内存区对象主要涉及下面两个步骤:
shm_open利用tmpfs技术将一段物理内存区域模拟成磁盘文件。
shm_open最主要的操作也是默认的操作就是在/dev/shm/下面打开或创建一个共享内存区。
SHM_OPEN(3) Linux Programmer's Manual SHM_OPEN(3) NAME shm_open, shm_unlink - create/open or unlink POSIX shared memory objects SYNOPSIS #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); Link with -lrt. **参数**: 1. name:共享内存区的名字 2. oflag:标志位,参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC. 3. mode:权限位,它指定O_CREAT标志的前提下使用 **返回值:**shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。
相关函数:
mmap函数把一个文件或者一个Posix共享内存区对象映射至调用进程的地址空间
MMAP(2) Linux Programmer's Manual MMAP(2) NAME mmap, munmap - map or unmap files or devices into memory SYNOPSIS #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); See NOTES for information on feature test macro requirements. **参数**: 1. addr:可以指定描述符fd应被映射到进程内空间的起始地址,它通常被指定为一个空指针,这样告诉内核自己去选择起始 地址,无论哪种情况下,该函数的返回值都是描述符fd所映射到内存区的其实地址。这里需要注意的是,文件需要 初始化长度,否则对内存操作时会产生SIGBUS信息(硬件错误)。 2. length:映射到调用进程地址空间中字节数,它从被映射文件开头offset个字节出开始算。offset通常设置为0 3. prot:内存映射区的保护由port参数指定,通常设置为PROT_READ | PROT_WRITE(可读与可写) PORT_READ -> 可读 PORT_WRITE -> 可写 PORT_EXEC -> 可执行 PORT_NONE -> 数据不可访问 4.用于设置内存映射区的数据被修改时,是否改变其底层支撑对象(这里的对象是文件),MAP_SHARED和MAP_PRIVATE必须指 定一个。 MAP_SHARED -> 变动是共享的 MAP_PRIVATE -> 变动是私自的 MAP_FIXED -> 准确的解析addr参数 **返回值:**若成功则为被映射区的起始地址,若出错则为MAP_FAILED。
**相关函数:**munmap:用于释放mmap所映射的内存区域
ps:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/file.h> #include <sys/wait.h> #define MAXSIZE 1024*1024*24 /*共享内存的大小,建议设置成内存页的整数倍*/ #define FILENAME "myshm" int main() { /* 创建共享对象,可以查看/dev/shm目录 */ int fd = shm_open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777); if (fd == -1) { perror("open failed:"); exit(1); } /* 调整大小 */ if (ftruncate(fd, MAXSIZE) == -1) { perror("ftruncate failed:"); exit(1); } /* 获取属性 */ struct stat buf; if (fstat(fd, &buf) == -1) { perror("fstat failed:"); exit(1); } printf("the shm object size is %ld\n", buf.st_size); void *ptr = mmap(0, MAXSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) error_out("MMAP"); close(fd); pid_t pid = fork(); if (pid == 0) { u_long *d = (u_long *)ptr; *d = 0xdeadbeef; exit(0); } else { int status; waitpid(pid, &status, 0); printf("child wrote %#lx\n", *(u_long *)ptr); } sleep(50); if (munmap(ptr, MAXSIZE) != 0) error_out("munmap"); /* 如果引用计数为0,系统释放内存对象 */ if (shm_unlink(FILENAME) == -1) { perror("shm_unlink failed:"); exit(1); } printf("shm_unlink %s success\n", FILENAME); return 0; }
程序解析:
写数据进程:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 1024*4 /*共享内存的大小,建议设置成内存页的整数倍*/ #define FILENAME "shm.test" int main() { /* 创建共享对象,可以查看/dev/shm目录 */ int fd = shm_open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777); if (fd == -1) { perror("open failed:"); exit(1); } /* 调整大小 */ if (ftruncate(fd, MAXSIZE) == -1) { perror("ftruncate failed:"); exit(1); } /* 获取属性 */ struct stat buf; if (fstat(fd, &buf) == -1) { perror("fstat failed:"); exit(1); } printf("the shm object size is %ld\n", buf.st_size); /* 建立映射关系 */ char *ptr = (char*)mmap(NULL, MAXSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap failed:"); exit(1); } printf("mmap %s success\n", FILENAME); close(fd); /* 关闭套接字 */ /* 写入数据 */ char *content = "hello readprocess!!!"; strncpy(ptr, content, strlen(content)); sleep(30); return 0; }
读进程:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #include <stdlib.h> #include <string.h> #define FILENAME "shm.test" int main() { /* 创建共享对象,可以查看/dev/shm目录 */ int fd = shm_open(FILENAME, O_RDONLY, 0); if (fd == -1) { perror("open failed:"); exit(1); } /* 获取属性 */ struct stat buf; if (fstat(fd, &buf) == -1) { perror("fstat failed:"); exit(1); } printf("the shm object size is %ld\n", buf.st_size); /* 建立映射关系 */ char *ptr = (char*)mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap failed:"); exit(1); } printf("mmap %s success\n", FILENAME); close(fd); /* 关闭套接字 */ printf("the read msg is:%s\n", ptr); sleep(30); return 0; }
先执行写进程,再执行读进程:读进程就会打印出"the read msg is hello readprocess!!!"
注意上面读写进程是先写后读,因此不用进行进程同步控制,若两进程同步执行需要加一个读写锁或其他方式来进行进程同步控制