# 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);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://linuxprogramming.weijun-lin.top/6.-nei-cun-ying-she.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
