必须用golang.org/x/sys/unix.Mmap(Unix)或github.com/edsrzf/mmap-go(跨平台),因标准库syscall.Mmap在Go 1.17+已弃用,Windows上直接panic,Unix下易因页对齐或cap错误触发SIGBUS。

Go 语言 mmap 写大文件必须用 golang.org/x/sys/unix.Mmap(Unix)或 github.com/edsrzf/mmap-go(跨平台)
标准库 syscall.Mmap 在 Go 1.17+ 已被标记为 deprecated,且在 Windows 上直接 panic;Unix 系统下也容易因页对齐、cap 错误触发 SIGBUS。硬核写法不能绕过平台差异。
实操建议:
- Linux/macOS:用
golang.org/x/sys/unix.Mmap,它封装了mmap(2)的参数校验和错误映射,比裸syscall更可靠 - Windows 或需跨平台:别自己封装
CreateFileMapping,直接上github.com/edsrzf/mmap-go——它内部处理了MAP_POPULATE、长度对齐、msync兼容性等细节 - 不要尝试用
reflect.SliceHeader手动构造切片头:Go 1.21+ 已禁止 unsafe 转换未对齐指针,且len/cap不匹配必 crash
mmap-go 中 mm.Bytes() 是唯一安全转切片的方式
系统 mmap 返回的原始内存地址,Go 运行时无法感知其长度。直接用 []byte{} 包裹或 unsafe.Slice 时若 offset + length 超出映射范围,访问即 SIGBUS。
正确做法只有这一种:
立即学习“go语言免费学习笔记(深入)”;
mm, err := mmap.Open("data.bin", os.O_RDWR, 0)
if err != nil {
log.Fatal(err)
}
defer mm.Unmap()
// ✅ 安全:Bytes() 内部已校验 len/cap,返回可读写切片
data := mm.Bytes()
data[0] = 0xff // 直接写,无 panic 风险
注意点:
-
mm.Bytes()返回的是完整映射区的切片;如只需某段,用data[offset:offset+size]切,不是重新unsafe.Slice - 如果文件被外部截断或磁盘写满,后续访问
data仍会SIGBUS——mmap 不做运行时长度防护 - Windows 下
mmap-go用MapViewOfFile模拟行为,但不支持MAP_SYNC,慎用于强一致性场景
MAP_PRIVATE vs MAP_SHARED 决定数据是否落盘
这是最常被忽略的语义陷阱:默认 MAP_PRIVATE 修改不会写回文件,崩溃即丢数据;而 MAP_SHARED 才真正实现“内存即文件”。
关键区别:
-
MAP_PRIVATE:写操作触发写时复制(COW),只改进程私有页,msync无效,关程序就丢 -
MAP_SHARED:写操作直接脏页,内核按需刷盘;调用msync(MS_SYNC)可强制同步(但性能跌至WriteAt水平) -
MAP_SYNC(仅 XFS/Btrfs 支持):写即落盘,但开销极大,且mmap-go当前不暴露该 flag
生产环境建议:
- 日志类追加写:用
MAP_SHARED+ 定期msync(MS_ASYNC),平衡可靠性与吞吐 - 临时计算中间结果:用
MAP_PRIVATE,避免污染原文件 - 绝对不允许丢数据?别用 mmap,老实用
os.File.WriteAt+fsync
随机写 >1GB 文件时 mmap 吞吐高 3–5 倍,但小写频繁反拖慢
优势来自内核页缓存机制:mmap 写是纯内存操作,缺页时由内核异步加载/刷盘;WriteAt 每次都要走 VFS、拷贝用户态 buffer、可能阻塞等待 IO。
但代价明显:
- 每字节写一次(如循环
data[i] = x)会引发大量缺页中断,比WriteAt慢 2–3 倍 - 首次访问未加载的页会卡住 goroutine,
MAP_POPULATE可预加载,但吃内存 - 映射超大文件(如 100GB)时,虚拟地址空间不紧张,但 RSS 可能暴涨——内核要维护页表项
优化方向:
- 批量写:把修改攒成块(如 4KB 对齐),再 memcpy 或循环赋值
- 预热:用
madvise(MADV_WILLNEED)(Unix)提示内核提前加载热点页 - 监控:
cat /proc/[pid]/maps查看实际映射大小,vmstat观察 pgpgin/pgpgout 是否异常高
真正硬核的地方不在调用 mmap,而在理解页生命周期——你写的每个字节,最终由谁、何时、以何种路径落到磁盘,这才是决定成败的边界。


















