
为什么 mmap 是处理大文件的首选方案
因为 read() 或 fread() 会把数据从内核缓冲区拷贝到用户空间,对 GB 级文件反复调用就是灾难;mmap() 让虚拟内存直接指向磁盘页,读写即访问内存地址,零拷贝、按需加载、支持随机访问——前提是你的场景不依赖强实时或频繁小块写入。
注意:Windows 没有 mmap(),得用 CreateFileMapping() + MapViewOfFile(),但 Linux/macOS 下统一用 mmap() 更直接。
mmap() 最简安全调用要填对哪几个参数
新手常卡在 prot 和 flags 组合上,比如只读文件却传了 PROT_WRITE,或想修改却忘了加 MAP_SHARED。
-
prot:只读文件用PROT_READ;需写入且同步回磁盘用PROT_READ | PROT_WRITE -
flags:必须选MAP_PRIVATE(写时复制,不影响原文件)或MAP_SHARED(写入直接落盘);MAP_POPULATE可预读,但会阻塞,慎用 -
fd:必须是已打开的、支持 mmap 的文件描述符(普通磁盘文件可以,管道/终端不行) -
offset:必须是页对齐(通常getpagesize()的整数倍),否则mmap()失败返回MAP_FAILED
示例:
int fd = open("big.bin", O_RDONLY);<br>size_t len = lseek(fd, 0, SEEK_END);<br>void* addr = mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0);<br>if (addr == MAP_FAILED) { /* 处理错误 */ }
立即学习“C++免费学习笔记(深入)”;
写入大文件时 msync() 和 munmap() 的调用时机
用 MAP_SHARED 写完不调 msync(),数据可能还在页缓存里,进程崩溃或系统断电就丢;但频繁 msync() 会拖慢性能,尤其小块写。
- 批量写入后、关键节点前调一次
msync(addr, len, MS_SYNC)(强制刷盘)或MS_ASYNC(后台刷) -
munmap()不负责刷盘,只解映射;它可以在msync()后任意时间调,但不能在msync()前释放addr - 进程退出时内核会自动清理映射,但显式
munmap()更可控,避免长期占用虚拟内存区域
常见错误:munmap() 后还访问 addr → Segmentation fault;msync() 传错长度(小于实际写入范围)→ 部分数据未落盘。
超过 2GB 文件在 32 位环境或某些 libc 上的兼容问题
size_t 在 32 位系统上只有 4 字节,而 mmap() 的 len 参数类型正是 size_t,所以理论上最大映射 4GB —— 但实际常卡在 2~3GB,因为用户空间地址空间被栈、堆、共享库挤占。
- 64 位编译是根本解法:
g++ -m64,确保sizeof(size_t) == 8 - 不要试图单次
mmap()整个 10GB 文件;改用分段映射:每次映射 128MB,处理完munmap()再映射下一段 - 检查
getconf PAGE_SIZE和ulimit -v(虚拟内存限制),有些容器环境默认设得很低
容易被忽略的一点:mmap() 成功不代表物理内存已分配,真正触发缺页中断是在首次访问对应地址时——所以“映射成功”不等于“数据已加载”,延迟可能出现在第一次读取某块时。






