mmap读大文件更快是因为将文件逻辑地址直接映射进进程虚拟内存,后续访问等价于读内存:不触发系统调用、不拷贝数据到用户缓冲区、还能靠内核页缓存自动预取。

直接用 mmap 读大文件(>1MB)确实比 fread 快,但前提是映射配置正确、访问模式匹配,否则反而更慢甚至崩溃。
为什么 mmap 读大文件更快?
因为 mmap 把文件逻辑地址直接映射进进程虚拟内存,后续访问等价于读内存:不触发系统调用、不拷贝数据到用户缓冲区、还能靠内核页缓存自动预取。但这个优势只在以下情况成立:
- 文件是普通磁盘文件(不是
/proc或管道) - 你做的是随机访问或重复扫描,而不是顺序读几 KB
- 页面已加载或你主动预热(如加
MAP_POPULATE) - 映射长度合理,不浪费 TLB 条目(小文件别 mmap)
必须检查的三个返回值
mmap 失败不会抛异常,而是返回 MAP_FAILED;忽略它会导致后续解引用直接 SIGSEGV。同样要检查 open 和 fstat 的返回值:
-
open("data.bin", O_RDONLY)返回 -1?说明路径错、权限不足或文件被锁 -
fstat(fd, &sb)返回 -1?常见于伪文件(/proc)、NFS 挂载异常或 fd 已关闭 -
mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0)返回MAP_FAILED?优先查errno:EINVAL(offset未页对齐)、ENOMEM(地址空间不足)、EPERM(O_RDONLY却要求PROT_WRITE)
避免 SIGBUS 的边界控制
SIGBUS 几乎都源于越界访问无效映射区域,不是代码逻辑错,而是映射与使用不一致:
- 映射长度不能超过
sb.st_size—— 别信lseek(fd, 0, SEEK_END),它对稀疏文件或某些设备文件返回错误大小 - 访问指针必须满足:
ptr + offset (mapped_addr) + mapped_len,建议封装为带边界检查的访问器 - 文件被其他进程
truncate()或rm + recreate后,原映射区域可能部分失效;可用mincore()定期探测页状态 - 不要用
memcpy(dst, src, huge_len)一次性扫整个映射区——若某页尚未加载且磁盘慢,会卡住整个线程
提升稳定性的实操细节
真正影响落地效果的,往往不是“能不能映射”,而是这些容易被跳过的点:
- 用
sysconf(_SC_PAGE_SIZE)获取页大小,别硬写4096(ARM64 服务器可能是65536) - 读取固定结构体时,必须
static_assert(std::is_standard_layout_v<mystruct>)</mystruct>,并校验sizeof(MyStruct)和字段偏移(#pragma pack会破坏对齐) - 映射后立即调
madvise(mapped_addr, len, MADV_RANDOM),关掉内核默认的顺序预读,避免随机访问时拖累性能 - 释放顺序:先
munmap(),再close(fd);虽然关 fd 不影响映射,但提前关可能干扰调试工具识别资源泄漏
最常被忽略的是:映射后不做任何预热就直接遍历整个区域,结果大量软缺页集中爆发,表现比 read() 还卡。真要极速读,得配合 MAP_POPULATE 或手动 mlock()(注意权限),而不是只依赖“映射了就等于能飞”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











