# 7.4 POSIX 共享内存

前面提到的内存映射允许无关进程共享内存区域以执行相应的 IPC，但具有一些缺点，比如它需要在硬盘上创建相应的文件，存在额外的 IO 开销。为了克服这些缺点，提出了 共享内存 API。 使用 POSXI 共享内存一般需要以下步骤：

1. 使用 `shm_open`函数打开一个共享内存对象，此函数返回一个引用该对象的文件描述符。
2. 将上一步获得的文件描述符传入 `mmap`调用并指定 flags 参数中指定 MAP\_SHARED，即可将共享内存对象映射到虚拟地址空间。此文件描述符上可以调用传统 Unix 文件描述符系统调用。

### 创建共享内存对象

`shm_open` 函数创建和打开一个新的共享内存对象或打开一个既有对象，它的参数与 open 函数类似：

```c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

/*
@brief	打开一个文件
@param	name		标识出了待创建或待打开的共享内存对象
@param	flags		掩码表，可取：
                	- O_CREAT  对象不存在时创建，若同时指定 O_EXCL，则对象存在时返回错误
                        - O_RDONLY 只读访问
                        - O_RDWR   读写访问
                        - O_TRUNC  将对象截断为 0
@param	mode		标识文件的所有权限，在创建共享内存对象时使用
			仅仅打开一个已有对象需要将其设置为 0
                	- S_IRUSR | S_IWUSR 属主的读写权限
                        - S_IRGRP | S_IWGRP	数组的读写权限
                        - S_IROTH | S_IWOTH	其它的去写权限
@return	成功返回文件描述符，失败返回 -1
*/

int shm_open(const char *name, int oflag, mode_t mode);
```

`shm_open`返回的文件描述符会设置 close-on-exec 标记，因此当程序执行了一个 exec 时文件描述符会被自动关闭。 共享内存对象在创建后其初始长度会被设置为 0，所以在调用 `mmap` 之前调用 `ftruncate`来设置对象的大小。并且在内存映射后继续调用 `ftruncate` 来操作映射内存大小。

### 删除共享内存对象

SUSv3 要求 POSIX 共享内存对象至少具备内核持久性，即它们会持续存在直到被显式删除或系统重启。当不再需要一个共享内存对象时就应该使用`shm_unlink`删除它：

```c
#include <sys/mman.h>

int shm_unlink(const char *name);
```

`shm_unlink`函数会删除通过 name 指定的共享内存对象。删除一个共享内存对象不会影响对象的既有映射（它会保持有效直到相应的进程调用 `munmap` 或终止），但会阻止后续的 `shm_open` 调用打开这个对象。一旦所有进程都解除映射这个对象，对象就会被删除，其中的内容会丢失。
