Skip to main content

Memory Map mmap

mmap 的本质:它将一个文件(Everything is a file,设备亦是如此)的数据,直接映射到进程的虚拟内存地址空间中。进程像读写普通内存一样读写这块内存,内核会自动将这些操作同步到文件或设备上。

#include <sys/mman.h>

void *mmap(size_t length;
void addr[length], size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(size_t length;
void addr[length], size_t length);

addr (起始地址):

  • 指定映射区域在进程虚拟地址空间中的期望起始地址。
  • 通常设置为 NULL。设置为 NULL 表示让操作系统内核自己去选择一个合适的、未被使用的虚拟地址进行映射(这是最推荐和最安全的做法)。如果非要指定,该地址通常需要是页大小(Page Size,通常是 4KB)的整数倍。

length (映射长度):

  • 表示要映射的内存区域的大小(以字节为单位)。
  • 注意:内核的映射是以页(Page)为单位的,如果你请求的大小不是页的整数倍,内核会自动向上取整到一页的大小,但超出 length 的部分虽然会被映射,但访问它可能会导致 SIGBUS 信号。

prot (内存保护标志):

  • 指定映射区域的访问权限,通常由以下几个常量按位或(|)组合而成:
    • PROT_READ: 映射区域可被读取。
    • PROT_WRITE: 映射区域可被写入。
    • PROT_EXEC: 映射区域可被执行(通常用于动态链接库映射)。
    • PROT_NONE: 映射区域不可访问。
  • 注意:指定的权限不能超过文件被打开(open)时的权限。例如,文件如果是只读打开的,这里就不能包含 PROT_WRITE

flags (映射类型和行为):

  • 控制映射对象的更新是否对映射同一对象的其他进程可见,以及更新是否传递到底层文件。必须包含以下两者之一:
    • MAP_SHARED: 共享映射。对这段内存的修改会同步到底层文件中,并且其他也映射了该文件的进程也能看到这些修改。常用于进程间通信(IPC)。
    • MAP_PRIVATE: 私有映射。采用写时复制(Copy-On-Write)技术。对内存的修改只对当前进程可见,不会同步回底层文件。常用于加载共享库或读取文件内容。
  • 常用的其他标志(可按位或):
    • MAP_ANONYMOUS (或 MAP_ANON): 匿名映射。不依赖于任何实际的文件,fd 会被忽略(通常设为 -1)。这主要用于像 malloc 那样分配大块的纯内存(通常大于 MMAP_THRESHOLD,比如 128KB)。
    • MAP_FIXED: 强制内核使用 addr 指定的具体地址,如果该地址不可用则映射失败。极其危险,不建议普通开发使用。

fd (文件描述符):

  • 要被映射的文件的文件描述符(由 open() 返回)。
  • 如果使用了 MAP_ANONYMOUS 标志进行匿名映射,则此参数应该设为 -1

offset (文件偏移量):

  • 表示从文件的哪个位置开始映射。
  • 极其重要的一点:offset 必须是系统页大小(Page Size,可通过 sysconf(_SC_PAGE_SIZE) 获取,通常为 4096 字节)的整数倍。
  • 如果是匿名映射(MAP_ANONYMOUS),此参数被忽略,通常设为 0