Posix 共享内存区
- 共享内存区是可用 IPC 形式中最快的。
- 一旦内存区映射到共享它的进程的地址空间,这些进程间的数据传递就不需要经过内核了;但是读写内存区时,需要进行同步。
- 非共享缓冲区读文件传给另一个进程写:4 次内核与内核的交互
- 共享缓冲区读文件传给另一个进程写:2 次内核与进程的交互
1. 相关函数
1.1 mmap
- mmap: 把一个文件或一个 Posix 共享内存区对象映射到调用进程的地址空间。
- 使用此函数有 3 个目的:
- 使用普通文件以提供内存映射 I/O;
- 使用特殊文件以提供匿名内存映射;
- 使用 shm_open 以提供无亲缘关系进程间的 Posix 共享内存区。
- 映射文件示意图:
#include <sys/mman.h>
// addr: 指定 fd 映射到的进程内空间的起始地址;
// len: 映射的长度——字节数;
// offset: 从被映射文件开头起 offset 字节开始映射;
// prot: 属性/模式 (PROT_READ|PROT_WRITE|PROT_EXEC|PROT_NONE)
// * PROT_NONE Pages may not be accessed.
// * PROT_READ Pages may be read.
// * PROT_WRITE Pages may be written.
// * PROT_EXEC Pages may be executed.
// flag: 很多歌,具体见 man
// * MAP_PRIVATE: 变动私有,不改变低层支撑对象;
// * MAP_SHARED: 变动共享,其他进程可见,改变低层支撑对象;
// * MAP_FIXED: 准确地解释 addr 参数;【???】
// 返回:成功——映射起始地址;失败——MAP_FAILED,设置 errno
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
1.2 munmap
- munmap: 从某个进程的地址空间删除一个映射关系。
#include <sys/mman.h>
int munmap(void *addr, size_t len);
1.3 msync
- msync: 同步内存与硬盘上的内容。
#include <sys/mman.h>
// flags:
// * MS_ASYNC: 执行异步写(写操作入内核队列,就返回)
// * MS_SYNC: 执行同步写(写操作完成后才返回)
// (以上两个指定一个,但不能都指定)
// * MS_INVALIDATE: 使高速缓存的数据失效
int msync(void *addr, size_t len, int flags);
2. 文件内存映射
内存映射一个普通文件时,内存中映射区的大小 (mmap 的第 2 个参数) 通常等于改文件的大小。 详见示例
3. 匿名内存映射
使用非匿名内存映射时,需要在文件系统中创建一个文件,进行 open 并 write 一些数据进行初始化。 如果目的是提供一个父子进程共享的内存映射,匿名内存映射则能简化上述流程。 创建匿名映射的方法:
- mmap 的 flag 参数指定
MAP_SHARED|MAP_ANON
, fd = -1。 - 这样的内存区会被初始化为 0; 详见示例
A. 注意
- 从移植性考虑,MAP_FIXED 不应该指定。
- 可移植的代码,应该把 addr 指定为 NULL,并且不指定 MAP_FIXED。
- mmap 成功返回后,fd 可关闭。
- 不是所有文件都能 mmap。