mmap读大文件更快因直接映射至虚拟内存,避免系统调用与数据拷贝,并利用页缓存预读;但需注意页对齐、文件大小、地址空间限制及正确使用MAP_POPULATE等细节。

为什么 mmap 读大文件比 read() 快?
因为 mmap 把文件直接映射进进程虚拟内存,后续访问就像读内存一样——不触发系统调用、不拷贝数据到用户缓冲区、还能利用内核页缓存自动预读。但前提是:你得用对,否则反而更慢甚至崩溃。
常见错误现象:Segmentation fault(没检查 mmap 返回值)、Invalid argument(文件大小为 0 或 offset 未按页对齐)、读写后文件没更新(忘了 msync 或 MAP_SHARED 没设对)。
- 必须用
open()以O_RDWR打开文件才能写;只读用O_RDONLY+PROT_READ -
offset参数必须是页大小(通常是 4096)的整数倍,否则mmap失败 - 映射长度不能超过文件当前大小(除非先用
ftruncate()扩容) - 小文件(mmap 反而可能更慢——页表建立开销压倒收益
怎么安全地用 mmap 写文件并保证落盘?
很多人以为只要改了映射地址里的字节,文件就自动更新了。错。是否落盘取决于 flags 和是否调用 msync。
使用场景:需要频繁随机修改大日志/数据库索引文件,又不想每次 write() 都走内核路径。
立即学习“C++免费学习笔记(深入)”;
- 写入必须用
MAP_SHARED,MAP_PRIVATE是写时复制,改了也不影响原文件 - 关键一步:修改后调用
msync(addr, length, MS_SYNC),否则可能只在页缓存里,断电就丢 - 如果程序异常退出,且没
msync,内核会在内存压力大时才回写——不可控 - 不要在
mmap区域里用memcpy跨越映射边界——可能触发SIGBUS
munmap 后还能访问映射内存吗?
不能。一旦调用 munmap,该地址空间立刻失效,再读写就是 Segmentation fault。这不是“延迟释放”,是立即解绑。
容易踩的坑:多线程环境下,一个线程 munmap 了,另一个线程还在用指针——典型 UAF(Use-After-Free)。
-
munmap成功后,对应地址变成非法访问区域,和free()后访问堆内存一样危险 - 别依赖
fork()子进程继承映射——子进程会继承,但父子写同一块MAP_SHARED区域时需自己加锁 - 映射失败时
mmap返回MAP_FAILED(即(void *)-1),不是NULL,务必用== MAP_FAILED判断
用 mmap 处理超大文件(>2GB)要注意什么?
32 位程序天然受限于 4GB 地址空间,留给 mmap 的连续空闲虚拟内存往往不够。64 位没问题,但仍有细节要抠。
性能影响:映射 100GB 文件不会立刻占满物理内存,但会占用大量虚拟地址空间和页表项——可能触发 OOM Killer 杀进程。
- 优先用
MAP_POPULATE(配合MAP_LOCKED更稳),避免首次访问时缺页中断卡顿 - 不要一次性映射整个文件;按需分段映射(比如每次映射 128MB),用完
munmap掉 - 检查
/proc/sys/vm/max_map_area,太小会导致mmap失败(Cannot allocate memory) - 文件路径必须是真实路径,符号链接要先
realpath(),否则mmap可能静默失败
最常被忽略的是:mmap 映射后,文件描述符可以关闭,但文件本身不能被 unlink() ——否则虽然映射还有效,但磁盘空间无法释放,直到进程退出。










