不能直接用 mmap 或 CreateFileMapping 封装跨平台内存映射,因二者语义不等价:mmap 支持匿名映射和 MAP_SHARED 同步写回,Windows 文件映射必须绑定真实文件且需 FlushViewOfFile + FlushFileBuffers 才能落盘;文件打开标志、页面对齐、映射/释放生命周期均需按平台差异化处理。

为什么不能直接用 mmap 或 CreateFileMapping 写业务逻辑
跨平台内存映射封装最常踩的坑,是把 Linux 的 mmap 和 Windows 的 CreateFileMapping 当成语义等价接口硬套。它们根本不是一回事:mmap 本质是虚拟内存页映射,支持匿名映射、MAP_SHARED 同步写回;Windows 的文件映射对象必须绑定真实文件句柄,且写入是否落盘依赖 FlushViewOfFile + FlushFileBuffers 组合,缺一不可。直接封装一层“统一接口”却忽略底层同步语义差异,会导致 macOS/Linux 上数据看似写入就生效,Windows 上却卡在缓存里不落盘——尤其在程序异常退出时丢数据。
open() / CreateFile() 的打开标志必须按平台差异化处理
文件打开方式直接影响后续映射行为。Linux 下 open() 必须带 O_RDWR(即使只读也要可写,因为 mmap 需要 PROT_WRITE 才能映射为可写视图);Windows 下 CreateFile() 的 dwDesiredAccess 必须与映射对象的 flProtect 匹配:若想映射为可写视图,这里必须传 GENERIC_READ | GENERIC_WRITE,否则 CreateFileMapping 会失败并返回 ERROR_INVALID_PARAMETER。
- Linux:
open(path, O_RDWR | O_CREAT, 0644),文件大小需提前ftruncate()对齐到页面边界(getpagesize()) - Windows:
CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr),之后必须调用SetFilePointerEx+SetEndOfFile扩展文件大小,不能只靠CreateFileMapping的dwMaximumSizeHigh/Low - macOS 注意:
open()后需fcntl(fd, F_NOCACHE, 1)禁用文件系统缓存,否则mmap可能读到脏页
映射视图的生命周期管理必须拆开“映射”和“视图”两层
Windows 要求先创建映射对象(HANDLE),再调用 MapViewOfFile 获取地址;Linux/macOS 的 mmap 一步到位。但真正麻烦的是释放顺序:Windows 必须先 UnmapViewOfFile,再 CloseHandle 映射对象;Linux 必须 munmap,然后 close fd。如果封装成单一 unmap() 方法,容易在 Windows 平台漏掉 CloseHandle,导致句柄泄漏——进程跑久了会触发 ERROR_TOO_MANY_OPEN_FILES 或 ERROR_NO_SYSTEM_RESOURCES。
- 推荐设计:类内持有一个
void* m_addr(映射起始地址)和一个平台相关句柄(int m_fdon Unix,HANDLE m_hMapon Windows) -
map()成功后,Unix 平台m_fd和m_addr均有效;Windows 平台m_hMap和m_addr均有效,m_fd为 -1 -
unmap()必须先释放视图(munmap/UnmapViewOfFile),再关闭底层资源(close/CloseHandle)
跨平台对齐和偏移必须用 sysconf(_SC_PAGESIZE) 而非硬编码 4096
虽然多数 x86_64 系统页面大小是 4096,但 ARM64 macOS 可能用 16KB,某些嵌入式 Linux 支持 64KB 大页。硬编码 4096 会导致 mmap 失败并返回 EINVAL,或 Windows 下 MapViewOfFile 返回 nullptr 且 GetLastError() 为 ERROR_MAPPED_FILE_SIZE_ZERO(实际是偏移未对齐)。所有偏移量(offset)、长度(length)和文件预分配大小,都必须向上对齐到运行时获取的页面大小。
立即学习“C++免费学习笔记(深入)”;
- 获取方式:
long page_size = sysconf(_SC_PAGESIZE);(Unix);Windows 用GetSystemInfo(&si); si.dwPageSize - 对齐宏建议:
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1)) - 特别注意:Windows 的
MapViewOfFile的dwNumberOfBytesToMap必须是页面大小的整数倍,且dwFileOffsetLow必须是页面大小的整数倍,否则调用失败
页面对齐这事看着小,但一旦出错,错误表现极隐蔽——比如只在某台 ARM 机器上偶尔 mmap 失败,查半天发现是偏移没对齐。


















