> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/sys/mmap.2.md).

# mmap(2)

`mmap` — 分配内存，或将文件或设备映射到内存中

## 名称

`mmap`

## 库

Lb libc

## 概要

`#include <sys/mman.h>`

```c
void *
mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
```

## 描述

`mmap()` 系统调用使从 `addr` 开始、最多延续 `len` 字节的页面从 `fd` 所描述的对象中映射，起始字节偏移为 `offset`。如果 `len` 不是页面大小的整数倍，映射区域可能延伸超出指定范围。任何超出映射对象末尾的延伸部分将以零填充。

如果 `fd` 引用普通文件或共享内存对象，则从 `offset` 开始、延续 `len` 字节的字节范围对于对象中可能（不一定为当前）的偏移必须是合法的。特别地，`offset` 值不能为负。如果对象被截断，且进程随后访问完全位于被截断区域内的页面，该访问将被中止，并向进程发送 `SIGBUS` 信号。

如果 `fd` 引用设备文件，`offset` 值的解释是设备特定的，由设备驱动程序定义。在这种情况下，虚拟内存子系统不对 `offset` 值施加任何限制，将其原样传递给驱动程序。

如果 `addr` 为非零，它将作为提示传递给系统。（为方便系统，区域的实际地址可能与所提供的地址不同。）如果 `addr` 为零，地址将由系统选择。返回该区域的实际起始地址。成功的 `mmap()` 将删除已分配地址范围内的任何先前映射。

保护权限（区域可访问性）通过在 `prot` 参数中对以下值进行*按位或*运算来指定：

**`PROT_NONE`** 页面不可访问。

**`PROT_READ`** 页面可读。

**`PROT_WRITE`** 页面可写。

**`PROT_EXEC`** 页面可执行。

除了这些保护标志外，FreeBSD 还能够为由 `mmap()` 分配、随后通过 [mprotect(2)](/sys/mprotect.2.md) 修改的区域设置最大保护权限。这通过将包装在 `PROT_MAX()` 宏中的一个或多个 `PROT_` 值进行*按位或*运算到 `prot` 参数中来实现。

`flags` 参数指定映射对象的类型、映射选项，以及对页面的映射副本所做的修改是进程私有的还是与其他引用共享。共享、映射类型和选项通过在 `flags` 参数中对以下值进行*按位或*运算来指定：

**`MAP_32BIT`** 请求位于当前进程地址空间前 2GB 内的区域。如果找不到合适的区域，`mmap()` 将失败。

**`MAP_ALIGNED`** (`n`) 在请求的边界上对齐区域。如果找不到合适的区域，`mmap()` 将失败。`n` 参数指定所需对齐方式的二进制对数。

**`MAP_ALIGNED_SUPER`** 对齐区域以最大化大页（“super”页面）的潜在使用。如果找不到合适的区域，`mmap()` 将失败。系统将根据映射的大小选择合适的页面大小。所使用的页面大小以及区域的对齐方式都可能受到被映射文件属性的影响。特别地，文件现有页面的物理地址可能要求特定的对齐。不保证区域在任何特定边界上对齐。

**`MAP_ANON`** 映射不与任何特定文件关联的匿名内存。用于创建 `MAP_ANON` 的文件描述符必须为 -1。`offset` 参数必须为 0。

**`MAP_ANONYMOUS`** 此标志与 `MAP_ANON` 相同，提供它是为了兼容性。

**`MAP_EXCL`** 此标志只能与 `MAP_FIXED` 组合使用。其效果描述请参见 `MAP_FIXED` 的定义。

**`MAP_FIXED`** 不允许系统选择与指定地址不同的地址。如果指定地址不可用，`mmap()` 将失败。如果指定了 `MAP_FIXED`，`addr` 必须是页面大小的倍数。如果未指定 `MAP_EXCL`，成功的 `MAP_FIXED` 请求将替换从 `addr` 到 `addr` + `len` 范围内进程页面的任何先前映射。相反，如果指定了 `MAP_EXCL`，则当该范围内已存在映射时，请求将失败。

**`MAP_GUARD`** 不创建映射，而是创建指定大小的保护区。保护区允许进程在其地址空间中创建预留，随后可由实际映射替换。除非请求指定 `MAP_FIXED`，否则 `mmap()` 不会在保护区的地址范围内创建映射。保护区可通过 [munmap(2)](/sys/munmap.2.md) 销毁。线程对受保护范围的任何内存访问都会导致向该线程发送 `SIGSEGV` 信号。

**`MAP_NOCORE`** 区域不包含在核心转储文件中。

**`MAP_NOSYNC`** 使通过此 VM 映射弄脏的数据仅在必要时（通常由分页器）刷新到物理介质，而非无谓地刷新。通常这会阻止更新守护进程刷新通过此类映射弄脏的页面，从而允许使用文件支持的共享内存映射在不相关的进程间高效共享内存。如果不使用此选项，你弄脏的任何 VM 页面都可能时不时地（通常每 30-60 秒）被刷新到磁盘，如果你不需要这种情况发生（例如当你为 IPC 目的使用共享的文件支持的 `mmap()` 区域时），可能会造成性能问题。当对象的所有映射被移除且引用该对象的所有描述符被关闭时，脏数据将自动刷新。请注意，无论是否使用 `MAP_NOSYNC`，VM/文件系统的一致性都会得到维护。此选项在 UNIX 平台间尚不可移植，尽管有些平台可能默认实现相同的行为。*警告！* 用 ftruncate(2) 扩展文件从而创建一个大空洞，然后通过修改共享 `mmap()` 来填充该空洞，可能导致严重的文件碎片化。为避免这种碎片化，你应该始终在通过 `mmap()` 修改新扩展区域之前，通过向新扩展区域写入零来预分配文件的后备存储。碎片化问题对 `MAP_NOSYNC` 页面尤为敏感，因为页面可能以完全随机的顺序刷新到磁盘。使用 `MAP_NOSYNC` 实现基于文件的共享内存存储时也是如此。建议通过向后备文件写入零来创建后备存储，而非使用 ftruncate()。你可以通过在顺序读取大文件时观察 `iostat 1` 的 KB/t（每次传输的千字节数）结果来测试文件碎片化，例如使用 `dd if=filename of=/dev/null bs=32k`。[fsync(2)](/sys/fsync.2.md) 系统调用将把与文件关联的所有脏数据和元数据（包括脏的 NOSYNC VM 数据）刷新到物理介质。sync(8) 命令和 [sync(2)](/sys/sync.2.md) 系统调用通常不刷新脏的 NOSYNC VM 数据。通常不需要 [msync(2)](/sys/msync.2.md) 系统调用，因为 BSD 实现了连贯的文件系统缓冲区缓存。但是，它可用于将脏 VM 页面与文件系统缓冲区关联，从而使其更快地刷新到物理介质。

**`MAP_PREFAULT_READ`** 立即更新调用进程的最低层虚拟地址转换结构（如页表），使区域内的每个内存驻留页面都映射为可读访问。通常这些结构是惰性更新的。此选项的效果是消除原本会在对该区域的初始读访问时发生的任何软故障。虽然此选项不排除 `prot` 包含 `PROT_WRITE`，但它不能消除对该区域初始写访问时发生的软故障。

**`MAP_PRIVATE`** 修改是私有的。

**`MAP_SHARED`** 修改是共享的。

**`MAP_STACK`** 创建一个按需向下增长的映射区域和一个相邻的保护区，保护区既为映射区域的增长预留地址空间，又限制映射区域的增长。映射区域和保护区合起来占用 `len` 字节的地址空间。保护区从返回的地址开始，映射区域在返回的地址加上 `len` 字节处结束。当访问保护区时，映射区域自动增大，保护区缩小相等的量。本质上，保护区和映射区域之间的边界向下移动，使访问落入扩大的映射区域内。但是，保护区绝不会缩小到小于 sysctl `security.bsd.stack_guard_page` 所指定的页数，从而确保在向下生长的映射区域与其下方最近的映射区域之间始终存在用于检测栈溢出的间隙。`MAP_STACK` 隐含 `MAP_ANON` 和 `offset` 为 0。`fd` 参数必须为 -1，`prot` 必须至少包含 `PROT_READ` 和 `PROT_WRITE`。保护区的大小（以页为单位）由 sysctl `security.bsd.stack_guard_page` 指定。

[close(2)](/sys/close.2.md) 系统调用不会取消页面映射，更多信息请参见 [munmap(2)](/sys/munmap.2.md)。

## 注释

虽然此实现对 `offset` 参数不施加任何对齐限制，可移植的程序必须只使用页面对齐的值。

大页映射要求支持对象的页面在虚拟地址空间和 RAM 中以匹配的块对齐。当映射一个在 RAM 中已由大页支持的对象时，系统会自动尝试使用大页映射，方法是在虚拟地址空间中对齐映射请求以匹配大物理页面的对齐方式。当映射对象中尚未由 RAM 中的页面支持的部分时，系统也可能使用大页映射。`MAP_ALIGNED_SUPER` 标志是一种优化，它将映射请求对齐到大页的大小，类似于 `MAP_ALIGNED`，区别在于如果对象已使用大页，系统将覆盖此对齐方式，使映射与现有大页保持一致。此标志主要用于在尚未在 RAM 中有页面的对象的首次映射时最大化大页的使用。

## 返回值

成功完成时，`mmap()` 返回指向映射区域的指针。否则，返回 `MAP_FAILED`，并设置 `errno` 以指示错误。

## 错误

`mmap()` 系统调用将在以下情况下失败：

**\[`EACCES`]** 在 `prot` 参数中指定了 `PROT_READ` 标志，但 `fd` 未打开以供读取。在 `flags` 和 `prot` 参数中指定了 `MAP_SHARED` 和 `PROT_WRITE` 标志，但 `fd` 未打开以供写入。

**\[`EBADF`]** `fd` 参数不是有效的打开文件描述符。

**\[`EINVAL`]** 当 `fd` 引用普通文件或共享内存时，`offset` 参数中传入了无效（负）值。

**\[`EINVAL`]** `prot` 参数中传入了无效值。

**\[`EINVAL`]** `flags` 参数中设置了未定义的选项。

**\[`EINVAL`]** 同时指定了 `MAP_PRIVATE` 和 `MAP_SHARED`。

**\[`EINVAL`]** 未指定 `MAP_ANON`、`MAP_GUARD`、`MAP_PRIVATE`、`MAP_SHARED` 或 `MAP_STACK` 中的任何一个。必须至少包含这些标志之一。

**\[`EINVAL`]** 指定了 `MAP_STACK` 但 `len` 小于或等于保护区大小。

**\[`EINVAL`]** 指定了 `MAP_FIXED` 但 `addr` 参数未页面对齐，或所需地址空间的一部分超出了用户进程的有效地址空间。

**\[`EINVAL`]** 同时指定了 `MAP_FIXED` 和 `MAP_32BIT`，且所需地址空间的一部分超出了用户地址空间的前 2GB。

**\[`EINVAL`]** `len` 参数等于零。

**\[`EINVAL`]** 指定了 `MAP_ALIGNED`，但所需对齐方式大于机器的虚拟地址大小或小于一个页面。

**\[`EINVAL`]** 指定了 `MAP_ANON` 但 `fd` 参数不为 -1。

**\[`EINVAL`]** 指定了 `MAP_ANON` 但 `offset` 参数不为 0。

**\[`EINVAL`]** 同时指定了 `MAP_FIXED` 和 `MAP_EXCL`，但请求的区域已被映射使用。

**\[`EINVAL`]** 指定了 `MAP_EXCL` 但未指定 `MAP_FIXED`。

**\[`EINVAL`]** 指定了 `MAP_GUARD`，但 `offset` 参数不为零、`fd` 参数不为 -1 或 `prot` 参数不为 `PROT_NONE`。

**\[`EINVAL`]** `MAP_GUARD` 与 `MAP_ANON`、`MAP_PREFAULT`、`MAP_PREFAULT_READ`、`MAP_PRIVATE`、`MAP_SHARED`、`MAP_STACK` 标志之一一起指定。

**\[`ENODEV`]** 未指定 `MAP_ANON`，且 `fd` 未引用普通或字符特殊文件。

**\[`ENOMEM`]** 指定了 `MAP_FIXED` 但 `addr` 参数不可用。指定了 `MAP_ANON` 但可用内存不足。

**\[`ENOTSUP`]** `prot` 参数包含的保护权限不是所指定最大保护权限的子集。

## 参见

[madvise(2)](/sys/madvise.2.md), [mincore(2)](/sys/mincore.2.md), [minherit(2)](/sys/minherit.2.md), [mlock(2)](/sys/mlock.2.md), [mprotect(2)](/sys/mprotect.2.md), [msync(2)](/sys/msync.2.md), munlock(2), [munmap(2)](/sys/munmap.2.md), [getpagesize(3)](/sys-1/getpagesize.3.md), [getpagesizes(3)](/sys-1/getpagesizes.3.md)

## 历史

`mmap` 系统调用首次在 4.2BSD 中文档化，并在 4.4BSD 中实现。

`PROT_MAX` 功能在 FreeBSD 13 中引入。
