安全使用内存映射需确保:文件以正确权限打开且句柄有效;映射大小不小于文件实际大小;写后调用flushviewoffile(windows)或msync(linux)确保落盘;卸载用unmapviewoffile/munmap而非free;多线程需同步;跨平台注意off_t与size_t类型一致性。

Windows 用 CreateFileMapping + MapViewOfFile 怎么安全读写
直接映射文件到进程地址空间,比 fread/fwrite 快,但容易崩在权限和同步上。关键不是“能不能映”,而是“映完怎么用不越界、不脏写、不丢数据”。
常见错误:用 GENERIC_WRITE 打开文件但映射时只传 PAGE_READONLY;或写完没调 FlushViewOfFile 就卸载,导致修改丢失;还有把 MapViewOfFile 返回的指针当普通堆内存 free —— 这会 crash。
- 文件必须以
CREATE_ALWAYS或OPEN_EXISTING打开,且句柄不能带FILE_FLAG_DELETE_ON_CLOSE - 映射对象大小(
dwMaximumSizeHigh/dwMaximumSizeLow)必须 ≥ 文件实际大小;若要扩展文件,先用SetFilePointerEx+SetEndOfFile,再映射 - 写入后务必调
FlushViewOfFile,否则修改可能卡在系统缓存里;卸载前用UnmapViewOfFile,别用free或delete - 多线程读写同一映射区?加
CRITICAL_SECTION或用Interlocked原子操作,别指望映射本身线程安全
Linux 用 mmap 映射文件要注意哪些坑
mmap 看似简单,但 MAP_SHARED 和 MAP_PRIVATE 行为差异极大,选错就白忙活。尤其注意 msync 的触发时机和 munmap 后指针失效问题。
典型翻车现场:用 MAP_PRIVATE 映射后改了内容,以为文件被改了,结果一查原文件纹丝不动;或者 mmap 返回 MAP_FAILED 却没检查,直接解引用导致段错误。
立即学习“C++免费学习笔记(深入)”;
-
fd必须是已打开的、支持 mmap 的文件(普通磁盘文件可以,管道/标准输入不行) - 写文件推荐
MAP_SHARED | MAP_SYNC(需内核 4.15+),否则至少得配msync(addr, length, MS_SYNC)才能确保落盘 - 映射长度不能为 0,且建议对齐到页边界(
getpagesize()),否则mmap可能截断或失败 -
mmap成功后,原fd可以close,不影响映射;但munmap后再访问该地址就是未定义行为 —— 不是空指针,是真崩溃
跨平台封装时,size_t 和 off_t 混用会出什么问题
Windows 的 CreateFileMapping 用两个 DWORD 表示大小,Linux 的 mmap 用 off_t 做偏移、size_t 做长度。在 64 位系统上,如果硬把 off_t 强转成 DWORD,大文件(>4GB)直接截断。
更隐蔽的是:某些旧版 MinGW 把 off_t 定义为 32 位,而 glibc 默认是 64 位。编译时看着没问题,运行到 4GB 文件就崩。
- 统一用
uint64_t存文件大小,在 Windows 上拆成高低 32 位传给CreateFileMapping - Linux 下确保编译加
-D_FILE_OFFSET_BITS=64,避免off_t被截短 - 映射前用
stat/GetFileSizeEx检查文件是否可映射(比如设备文件、网络文件系统可能不支持)
为什么改完映射区内容,ReadFile 或 lseek+read 读不到最新值
因为映射区和文件 I/O 是两套缓冲机制。你用 MapViewOfFile 写了,系统不一定立刻刷进文件;反过来,用 WriteFile 改了文件,映射区看到的还是旧内存页 —— 这不是 bug,是设计如此。
想让两者一致?要么全走 mmap 路线(写完 FlushViewOfFile + msync),要么全走传统 I/O。混用等于主动制造数据竞争。
- Windows 下,
FlushViewOfFile只保证写入系统缓存,要真正落盘还得FlushFileBuffers(对原始hFile调用) - Linux 下,
msync(MS_SYNC)才等同于fsync;MS_ASYNC只是提交写请求,不等待完成 - 不要在映射区里放 C++ 对象(比如含虚表、引用计数的类),因为构造/析构不会自动触发,内存布局也可能跨平台不一致










