mmap仅在随机访问大文件、多进程共享、避免拷贝三类场景有优势;顺序读小文件用read更高效,错误参数设置或忽略msync将导致段错误、数据不落盘或性能归零。

mmap 不是万能加速器,用错反而比 read/write 慢得多;它只在随机访问大文件、多进程共享、避免拷贝这三类场景下有明确优势。
什么时候该用 mmap,而不是 std::ifstream 或 read
核心判断依据是访问模式和数据规模:
- 顺序读一个 10MB 日志文件?
std::ifstream+read配合 64KB 缓冲区更稳,mmap带来额外页表开销,无收益 - 需要频繁跳转读取 TB 级数据库索引文件的任意 offset?必须用
mmap,否则每次lseek+read都触发内核拷贝和上下文切换 - 多个进程要同时读写同一配置段(如共享内存式通信)?
mmap+MAP_SHARED是唯一低开销方案 - 文件小于 4KB?直接
read更快——mmap 至少占用一个页(通常 4KB),小文件纯属浪费
mmap 调用时最关键的三个参数怎么设
错误设置会导致段错误、数据不落盘、或性能归零:
-
prot:读就只用PROT_READ,写才加PROT_WRITE;混用PROT_EXEC在现代系统上基本被禁用,且毫无必要 -
flags:只读场景一律用MAP_PRIVATE(写时复制,安全);需持久化修改必须用MAP_SHARED,否则msync无效 -
offset:必须是系统页大小整数倍(getpagesize()返回值,通常是 4096);传 123 会直接失败并返回MAP_FAILED
示例:映射文件第 2GB 处开始的 1MB 只读区域
立即学习“C++免费学习笔记(深入)”;
int fd = open("data.bin", O_RDONLY);
off_t offset = 2ULL * 1024 * 1024 * 1024; // 必须对齐
size_t length = 1024 * 1024;
void* ptr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, offset & ~(getpagesize()-1));
// 注意:offset 被手动对齐,不是直接传原值为什么改了映射内存,磁盘文件没更新
这是最常被忽略的持久化陷阱。即使用了 MAP_SHARED,操作系统也不会立即刷盘:
- 修改后不调
msync(ptr, len, MS_SYNC),数据只留在页缓存,进程退出或系统崩溃即丢失 -
MS_ASYNC仅触发后台刷盘,无法保证何时完成,适合日志类容忍丢失的场景 - 仅靠
munmap不触发同步,close(fd)也不管这事——mmap 和 fd 生命周期解耦 - 若需强一致性(如数据库 WAL),应在关键点显式
msync(..., MS_SYNC),并检查返回值是否为 0
局部映射和预取怎么配合才能真提速
全量映射 TB 文件会耗尽虚拟地址空间(尤其 32 位进程),而预取不加则顺序扫描仍卡在缺页中断上:
- 用
mio::make_mmap或手动mmap指定length和对齐offset,只映射当前需要的块(例如 128MB 片段) - 在循环中访问前,用
__builtin_prefetch(ptr + offset, 0, 3)提前加载后续缓存行;数字3表示“尽可能快地预取”,对顺序扫描最有效 - 避免在热循环里反复
mmap/munmap:映射/解映射本身有开销,应复用映射区域或按需滑动偏移 - 实测显示:10GB 文件顺序扫描,加预取 + 局部映射(每次 256MB)比全量映射快 1.7 倍,内存峰值降低 82%
真正难的不是调通 mmap,而是判断哪一段该映射、什么时候该预取、以及数据到底有没有真正落盘——这些决策没法自动化,得贴着业务逻辑算。



















