第6章 Posix共享内存区
6.1 共享内存区
共享内存区是可用IPC形式中最快的,只有映射和解除映射需要进入内核的系统调用,映射后对共享内存区的访问和修改不再需要系统调用(内核只要负责好页表映射和处理页面故障即可),但通常需要同步手段。
一个客户-服务器间传递文件数据的例子中,FIFO或消息队列等IPC方式需要4次内核-进程间的数据复制,每次都需要切换地址空间,开销很大;共享内存区只需要2次跨内核的数据复制。
6.2 mmap
mmap的三个目的:
1. 使用普通文件以提供内存映射I/O。
2. 使用特殊文件以提供匿名内存映射。
3. 使用shm_open以提供Posix共享内存区。
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
addr:调用者希望的起始地址,通常为空,表示由内核选择。
len:映射区大小。
prot:映射区的保护模式,PROT_READ | PROT_WRITE | PROT_EXEC | PROT_NONE。
flags:共享模式,MAP_SHARED和MAP_PRIVATE指定对映射区的数据修改会不会影响到底层支撑对象上;MAP_FIXED表示指定addr为起始地址。
fd和offset:用于支撑的底层文件范围。
mmap成功返回后可以关闭fd,不会影响映射。映射后对共享内存区的访问就被纳入到操作系统的虚拟内存的管理下,因此不再需要系统调用。
munmap用于从某个进程的地址空间删除一个映射关系。
msync用于同步一个指定了MAP_SHARED标志的映射区与支撑文件数据。
终端和套接字不能进行映射。
如果指定了MAP_SHARED,映射文件的实际内容就是共享内存区的初始内容。
6.3 匿名内存映射
如果只想在父子进程间使用共享内存区,可以用匿名内存映射来简化操作:
1. 4.4BSD匿名内存映射:将mmap的flags指定成MAP_SHARED | MAP_ANON,fd为-1。
2. SVR4:打开特殊设备/dev/zero,将得到的fd用于mmap。
6.4 映射区长度与文件大小
mmap返回的地址一般是页对齐的,映射区最后不满一页的部分可访问,但对它的修改不会写入文件,对超出映射区所在的页的访问会产生段错误。
若映射区大小超出文件大小,则访问在映射区内但超出文件范围的页面时会引发总线错误。
映射过程中如果底层文件大小改变,也会同样改变映射区的可访问范围。
6.5 Posix共享内存区
两种无亲缘关系进程间共享内存的方法:
1. 内存映射文件:open一个普通文件,用它的fd进行mmap。
2. 共享内存区对象:shm_open一个IPC名字,用它的fd进行mmap。
6.6 功能函数
int shm_open(const char *name, int oflag, mode_t mode);
创建或打开一个Posix共享内存区对象。Posix没有指定一个新建的共享内存区对象的初始内容。
int shm_unlink(const char *name);
引用计数删除。
int ftruncate(int fd, off_t length);
改变文件或共享内存区对象的大小。
int fstat(int fd, struct stat *buf);
获取文件信息,对共享内存区对象只有4个成员有信息:
st_mode:读写权限;
st_uid、st_gid:所有者身份;
st_size:共享内存区大小。