windows 无 mmap,需用 createfilemapping+mapviewoffile;二者语义相近但参数含义不同:dwmaximumsizehigh/low 对应 length,page_readwrite ≈ prot_read|prot_write,file_map_write 不等价于 map_shared,同步须显式 flushviewoffile;错误照搬 map_private 等参数会导致 error_invalid_parameter。

Windows 和 Linux 下 mmap 等价接口差异在哪
Windows 没有 mmap,得用 CreateFileMapping + MapViewOfFile;Linux 用 mmap 即可。二者语义接近但参数含义不同:比如 Windows 的 dwMaximumSizeHigh/dwMaximumSizeLow 对应 Linux 的 length,而保护标志上,PAGE_READWRITE ≈ PROT_READ | PROT_WRITE,但 FILE_MAP_WRITE 不等于 MAP_SHARED——它只控制映射权限,同步仍依赖 FlushViewOfFile 或关闭句柄。
常见错误是直接照搬 Linux 参数到 Windows,比如把 MAP_PRIVATE 当作可选项传给 Windows,结果调用失败且返回模糊的 ERROR_INVALID_PARAMETER。
- Windows 映射大小必须显式指定(创建时),Linux 可以映射文件当前长度或更大(访问越界会触发 SIGBUS)
- Linux
mmap返回void*,WindowsMapViewOfFile也返回void*,但 Windows 失败时返回NULL,Linux 失败时返回MAP_FAILED(即(void*)-1),判断方式不能统一用== nullptr - 打开文件句柄:Windows 需
GENERIC_READ | GENERIC_WRITE+CREATE_ALWAYS或OPEN_EXISTING;Linux 用open(),flag 要含O_RDWR(只读映射也建议开写权限,否则mmap可能失败)
如何封装统一的 C++ 类接口避免平台分支污染业务逻辑
核心是把平台差异收进构造函数和析构函数,对外暴露一致的 data()、size()、flush() 接口。不要在类里写 #ifdef _WIN32 分支逻辑——而是拆成两个私有实现类(MMAPImplWin / MMAPImplUnix),用 pimpl 模式或工厂函数隔离。
示例关键点:
- 构造函数接受路径、期望大小、是否只读;内部自动处理文件创建/截断(Linux 用
ftruncate,Windows 用SetFilePointerEx+SetEndOfFile) - 只读映射在 Windows 上需用
FILE_MAP_READ+PROT_READ,但注意:即使只读映射,Windows 创建文件映射对象时仍要求源文件句柄有GENERIC_WRITE权限(否则CreateFileMapping失败) -
flush()在 Linux 是空操作(msync非必需),Windows 必须调用FlushViewOfFile,否则修改可能未落盘
为什么 mmap 文件大小必须是系统页对齐,且未对齐时要手动补零
所有平台的内存映射都以页为单位(通常 4KB)。如果文件大小不是页对齐,mmap 或 MapViewOfFile 仍能成功,但末尾未对齐部分读出来是全零(Linux)或引发访问违规(Windows,取决于映射保护)。更麻烦的是:写入这部分区域不会写回文件——因为文件没那么长。
所以封装层必须做两件事:
- 构造时检查并按页向上取整(
(size + page_size - 1) & ~(page_size - 1)),然后用ftruncate或SetEndOfFile扩展文件 - 对外暴露的
size()返回用户请求的原始大小,而非映射长度;data()指针起始位置不变,但用户不应访问超出原始大小的内存 - 若用户传入大小为 0,Linux 允许映射,Windows 要求至少 1 字节,封装层需统一处理为最小页大小
跨进程共享时,Windows 命名映射对象要注意什么
Linux 的 mmap 直接操作文件描述符,天然支持多进程通过同一路径映射;Windows 的 CreateFileMapping 若想跨进程,必须传入非 NULL 的 lpName,且该名字全局可见(类似信号量名)。问题在于:名字冲突、权限、字符编码。
典型踩坑:
- 名字含反斜杠(如
"Local\MyMap")会被解释为对象管理器路径,跨会话(如服务 vs 用户桌面)不可见;应优先用"Global\..."或"Local\..."并确认会话上下文 - 名字不支持 Unicode 路径中的非 ASCII 字符(即使编译为 Unicode),建议只用 ASCII 字母/数字/下划线
- 多个进程用相同名字创建映射时,Windows 会复用已有对象(不报错),但若初始大小不一致,
CreateFileMapping返回已存在对象的句柄,而忽略新传入的大小——后续MapViewOfFile可能因视图大小超限失败 - Linux 无此机制,跨进程共享完全依赖文件路径和 open() 的 O_RDWR,更简单但也更难做原子初始化(比如首次创建时写默认数据)
实际项目中,如果只是单进程多线程用,Windows 传 NULL 名字最省事;真要跨进程,建议封装层加一层基于文件路径的哈希生成唯一映射名,并在首次创建时用文件锁保证初始化原子性——这点容易被忽略,但线上出问题时很难排查。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











