> For the complete documentation index, see [llms.txt](https://linuxprogramming.weijun-lin.top/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://linuxprogramming.weijun-lin.top/6.-nei-cun-ying-she.md).

# 6. 内存映射

### 概述

内存映射可用于 IPC 以及其他很多方面。主要通过 mmap 系统调用创建。 内存映射分为两种：

* 文件映射：将一个文件的一部分直接映射到虚拟内存中。
* 匿名映射：没有对应的映射文件，这种映射的分页会被初始化为 0

内存映射可被不同进程共享，一般有以下两种情况：

* 两个进程映射了同一个文件的同一个区域，此时它们会共享物理内存的相同分页
* 通过 fork 创建的子进程会继承父进程的映射副本

内存映射可以是私有或者共享的：

* 私有映射（MAP\_PRIVATE）：在映射上的操作其他进程不可见。内核会采取写时复制技术完成这个任务。
* 共享映射（MAP\_SHARED）：映射内容的变更对每一个进程可见，对于文件映射变更会发生在底层文件上，但不一定是立即发生。

各种映射的用途一般为： ![image.png](https://cdn.nlark.com/yuque/0/2023/png/29221640/1689154297014-3e72bd7b-346e-43ad-ace5-2c405839cf3b.png#averageHue=%23f1f0f0\&clientId=ua2453854-3fb3-4\&from=paste\&height=138\&id=u447543f0\&originHeight=241\&originWidth=1200\&originalType=binary\&ratio=1.75\&rotation=0\&showTitle=false\&size=59817\&status=done\&style=none\&taskId=u38a67b4a-9bdf-4ed8-9833-63c3268ae10\&title=\&width=685.7142857142857) 子进程在执行 exec 后，原有映射会丢失。

### 创建一个映射

mmap 系统调用可以创建一个映射：

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

/*
@brief 创建一个内存映射
@param addr	映射被放置的虚拟地址，一般取 NULL，让内核选择一个合适的地址
        	非 NULL 时，需要内核会为其舍入到最近的一个分页边界处，并且不能与现有映射冲突
@param length	指定了映射的字节数，会被提升为分页大小的倍数
@param prot	位掩码，映射的保护信息
            	- PROT_NONE	区域无法访问
                - PROT_READ	区域内容可读取
                - PROT_WRITE	区域内容可修改
                - PROT_EXEC     区域内容可执行
@param flags	控制映射操作的选项位掩码
            	- MAP_PRIVATE	创建私有映射
                - MAP_SHARED	创建共享映射
                - MAP_ANONYMOUS 创建匿名映射
@param fd	文件描述符，与 offset 一起用于文件映射，确定映射的开始位置
@param offset	文件偏移量，匿名映射将忽略这两个参数，也需要是分页大小的倍数
@return 成功返回映射的地址，否则返回 MAP_FAILED

*/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
```

### 解除映射区域

通过 munmap 调用解除映射区域：

```c
#include <sys/mmap>
// 解除 addr 处长度为 length 字节的映射，addr 是分页对齐的
// 一般 addr 是 mmap 返回的地址，length 是之前创建映射的大小
// 自己设置的 addr 会被舍入到分页大小的整数倍
int munmap(void *addr, size_t length);
```

### 同步映射区域

内核会自动将发生在 MAP\_SHARED 映射内容上的变更写入到底层文件中，但在默认情况下，内核不保证这种同步操作会在何时发生，可以通过 msync 调用显示控制。

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

/*
@brief 将 addr 处 length 大小字节同步到底层文件
@param flags 可取：
             - MS_SYNC		同步写入，阻塞直到所有被修改过的分页写入文件
             - MS_ASYNC 	异步的文件写入
             - MS_INVALIDATE 	上面的标志可以结合这个标识，使映射数据的缓存副本失效。
*/
int msync(void *addr, size_t length, int flags);
```
