内存映射写入二进制文件更快的本质是绕过内核缓冲区拷贝和系统调用开销;普通fwrite需用户数据→内核页缓存→磁盘且每次触发系统调用,而mmap通过虚拟内存映射实现写内存即写文件,依赖缺页和脏页回写机制,批量时延迟更低、吞吐更高。

内存映射写入二进制文件比普通 fwrite 快在哪?
本质是绕过内核缓冲区拷贝和系统调用开销。普通写入要经历:用户数据 → 内核页缓存 → 磁盘(可能延迟刷盘),每次 fwrite 都触发一次系统调用;而 mmap 把文件直接映射成进程虚拟内存,写内存即写文件,由缺页和脏页回写机制自动处理,批量写入时延迟更低、吞吐更高。
但注意:这不等于“一定更快”。小数据量(SIGBUS。
- 必须先用
open打开文件,再用ftruncate预设大小,不能靠mmap自动扩展文件 -
mmap返回的是void*,需按实际数据类型强转(如int32_t*),别直接当char*逐字节写——对齐和缓存行效率会掉 - 写完记得调用
msync(MS_SYNC)强制落盘,否则程序退出前崩溃可能丢数据
mmap 写二进制 .dat 文件的最小可靠流程
Windows 用 CreateFileMapping + MapViewOfFile,Linux/macOS 用 open + mmap。以 Linux 为例,核心四步不能少:
- 用
open("data.dat", O_RDWR | O_CREAT, 0644)获取 fd - 用
ftruncate(fd, file_size)把文件撑到目标长度(否则mmap可能失败或写崩) - 用
mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)映射,MAP_SHARED是关键——只有它才真正回写文件 - 写完调
msync(ptr, file_size, MS_SYNC),再munmap(ptr)和close(fd)
常见错误:MAP_PRIVATE 映射——改了内存但不会写入文件;ftruncate 忘了或传错 size,导致 mmap 成功但写到末尾时触发 SIGBUS。
立即学习“C++免费学习笔记(深入)”;
结构体数组写入时的对齐与 padding 风险
直接把 struct Record { int id; double value; } 数组往 mmap 区域 memcpy,很可能出错。编译器插入的 padding 字节在内存里存在,但若文件格式要求紧凑二进制布局(比如跨平台读取),这些 padding 就成了噪声。
- 用
#pragma pack(1)或[[gnu::packed]]消除 padding,但会降低 CPU 访问速度(非对齐访问在某些 ARM 上直接报错) - 更稳的方式:定义两个版本——内存中用自然对齐结构体操作,写入前用
memcpy拷贝到紧凑 buffer,再写入 mmap 区域 - 别依赖
sizeof(YourStruct)当文件记录长度,打印offsetof最后成员 + 其大小来验证实际占用
为什么多线程并发写同一块 mmap 区域容易崩
不是线程安全的。多个线程直接往同一个 int32_t* 地址写,即使加了 std::mutex,也只锁住了“写操作入口”,没锁住 CPU 缓存行刷新顺序。尤其在 NUMA 架构上,不同 socket 的 core 写同一 cache line,会引发严重 false sharing 和性能抖动。
- 每个线程应负责独立内存段:提前把 mmap 区域切分成固定大小块,用原子变量分配段号,避免竞争
- 绝对不要让多个线程写相邻地址(比如
arr[i]和arr[i+1]),cache line 通常是 64 字节,小心踩进同一行 - 如果必须聚合写,用本地 buffer 积累一批数据,再单次 memcpy 到 mmap 区域——减少映射区域的写频次
底层细节藏得深:mmap 映射页是按 4KB 对齐的,但 CPU 缓存行是 64 字节,这两层对齐不一致时,一个看似安全的 struct 写入,可能跨两个 cache 行,引发不可预测的同步延迟。



















