Memory Mapped Files And mmap
内存映射文件与 mmap
内存映射文件把文件的一段内容映射到进程的虚拟地址空间。映射建立后,程序可以像访问普通内存一样读写这段地址;文件数据的调入、写回由操作系统结合缺页机制完成。
1 | int fd = open("data.bin", O_RDWR); |
这里的 p 是一个虚拟地址。程序访问 p[i] 时,CPU 仍然发出虚拟地址;MMU 查页表;若对应文件页尚未在主存中,就触发缺页故障,由 OS 从文件读入对应页面。
mmap
mmap 的关键是建立映射关系:
$$
\text{进程虚拟页} \longleftrightarrow \text{文件中的某个页大小区间}
$$
一次典型访问过程:
open打开文件,得到文件描述符。mmap在进程虚拟地址空间中划出一段区域。- OS 记录这段虚拟地址范围对应哪个文件、从哪个文件偏移开始、长度是多少、允许读写还是只读。
- 程序访问映射区中的某个地址。
- 若对应页尚未调入主存,产生缺页故障。
- OS 根据映射关系,从文件中读入对应页,放入页框。
- OS 更新页表项,重新执行原来的访存指令。
mmap 和请求分页是同一套机制的自然延伸:普通匿名页的后备存储可以是对换区,而内存映射文件页的后备存储是文件。
传统文件访问常见流程是:
1 | open -> seek -> read/write -> close |
read 把文件内容读到用户缓冲区,程序再访问缓冲区;write 把用户缓冲区内容写回文件。
mmap 的流程是:
1 | open -> mmap -> 直接访问映射区 -> msync/munmap |
| 对比项 | read/write | mmap |
|---|---|---|
| 访问方式 | 显式系统调用读写文件 | 用普通内存读写指令访问映射区 |
| 数据调入时机 | 调用 read 时读入 |
访问尚未在主存的映射页时调入 |
| 地址空间 | 用户自己提供缓冲区 | OS 分配或使用指定虚拟地址范围 |
| 适合场景 | 顺序读写、简单 I/O | 大文件随机访问、共享文件、减少显式拷贝 |
mmap 的优势是 I/O 被放进虚拟存储和缺页处理流程中。程序不用反复调用 read、write 移动文件指针和搬运缓冲区。
共享映射与私有映射
mmap 的映射方式决定写入行为。
| 类型 | 含义 | 写入结果 |
|---|---|---|
MAP_SHARED |
多个进程可映射同一个文件区域 | 修改对其他共享映射进程可见,并可写回文件 |
MAP_PRIVATE |
私有映射,采用写时复制COW | 写入只影响当前进程,不直接修改原文件 |
共享映射常用于:
- 多个进程共享同一个文件内容。
- 共享内存通信。
- 数据库、日志、索引文件等需要频繁随机访问的场景。
私有映射常用于:
- 加载可执行文件和共享库的只读代码段。
- 进程私有数据页的写时复制。
- 需要从文件初始化内容,但修改不影响原文件的场景。
MAP_PRIVATE 也是按需调页: 当进程第一次写某个共享物理页时,才复制出私有页。
多个进程可以把同一个文件映射到各自的虚拟地址空间。它们看到的虚拟地址可以不同,但页表可以指向同一批物理页框。
当进程 A 修改共享映射中的某个页时:
- CPU 写入进程 A 的虚拟地址。
- 页表把该虚拟页翻译到共享物理页。
- 该物理页被标记为脏页。
- 进程 B 若映射同一文件页,也会通过自己的虚拟地址看到这份修改后的物理页。
因此,共享映射能实现进程间共享数据。但共享不等于自动同步访问顺序;多个进程同时读写共享区域时,仍然需要锁、信号量、互斥量等同步机制。
写回与解除映射
映射区被写入后,对应物理页会变成脏页。脏页何时回到磁盘,取决于映射类型和同步方式。
| 操作 | 作用 |
|---|---|
msync |
请求把映射区的修改同步到文件 |
munmap |
解除虚拟地址区间与文件的映射 |
close(fd) |
关闭文件描述符,不等于取消已有映射 |
需要区分两个关系:
- 文件描述符用于建立映射;映射建立后,关闭
fd不会让映射区立刻失效。 - 映射区由
munmap解除;进程退出时,OS 也会回收映射。
与共享库的关系
进程虚拟地址空间中常有“共享库的存储映射区”。动态库代码可以通过 mmap 类似机制映射进多个进程地址空间:
- 代码页通常只读,可以由多个进程共享同一份物理页。
- 每个进程看到的是自己的虚拟地址空间。
- 页表把不同进程的虚拟页映射到相同物理页框。
- 若某些数据页需要写入,可用写时复制保护进程间隔离。
这也是虚拟存储透明性的一个典型例子:进程像调用普通函数一样调用库函数,背后由装载器、页表和缺页机制完成库文件到内存页的映射与调入。
小结
mmap 的核心:
- 它把文件区间映射到进程虚拟地址空间。
- 文件页通常按需调入,第一次访问可能触发缺页故障。
- 共享映射可以让多个进程通过页表共享同一份物理页;私有映射则通过写时复制保护修改不影响原文件。