Go语言无跨平台mmap内置支持,syscall.Mmap在Windows会panic,Unix系统易因cap错误触发SIGBUS;mmap.Write更快因页缓存与延迟刷盘,内核按需回写,避免每次WriteAt的VFS拷贝开销。

Go 语言没有跨平台 mmap 内置支持,直接用 syscall.Mmap 在 Windows 上会 panic,Unix 系统也容易因切片 cap 错误触发 SIGBUS。
为什么 mmap.Write 大文件比 os.File.WriteAt 快?
核心在页缓存和延迟刷盘:mmap 把文件映射进虚拟内存,写操作本质是改内存页,由内核按需回写(msync 控制时机);而 WriteAt 每次都走 VFS 层、拷贝用户态数据到内核 buffer。随机写 >1GB 文件时,吞吐常高出 3–5 倍。
但要注意:
- 用
MAP_PRIVATE时,修改不自动落盘,崩溃或断电可能丢数据 -
MAP_SYNC可强制同步,但仅部分文件系统支持,性能退化接近WriteAt - 频繁小写(如每字节改一次)反而更慢——缺页中断开销太大
如何安全把 mmap 内存转成 Go 切片?
系统返回的原始字节切片 data 的 len 和 cap 默认为 0,不能直接用。错用 reflect.SliceHeader 或强制转换会导致 panic 或越界读。
立即学习“go语言免费学习笔记(深入)”;
推荐做法:
- 跨平台项目直接用
github.com/edsrzf/mmap-go,调mm.Bytes()即得已设好len/cap的安全切片 - Unix/Linux/macOS 手撸时,用
golang.org/x/sys/unix.Mmap+unsafe.Slice(Go 1.21+):unsafe.Slice(unsafe.Add(unsafe.Pointer(&data[0]), offset), length),但必须确保offset + length <= mm.Len() - 绝对不要跳过
cap设置——这是 SIGBUS 最常见源头
Windows 上怎么用 mmap?别自己封装 syscall
Go 官方 syscall 包没暴露 Windows 的 CreateFileMapping/MapViewOfFile,硬写极易出错(句柄泄漏、权限错配、对齐未处理)。
实操建议:
- 统一用
mmap-go库,它内部做了平台适配,API 一致,且处理了长度对齐、MAP_POPULATE优化等边界 - 若必须原生调用,改用
golang.org/x/sys/windows封装,但要手动管理UnmapViewOfFile+CloseHandle,并校验GetLastError - 测试阶段务必覆盖文件截断、磁盘满、并发写场景——Windows 对 mmap 的容错比 Unix 更弱
mmap 写入后为什么偶尔 panic: "signal SIGBUS"?
SIGBUS 几乎只发生在三类情况:
- 访问了
mmap范围外的地址(offset + length > mm.Len()) - 文件被外部程序截断或删除(尤其日志轮转场景)
- 磁盘空间不足,内核无法完成页回写
最容易被忽略的是:即使你只读,只要文件被截断,后续任何访问(包括 len(data))都可能 SIGBUS。生产环境必须监听 os.Interrupt 和 syscall.SIGUSR1 等信号,并在退出前调 msync + munmap;同时定期用 os.Stat 校验文件大小是否突变。


















