内存映射i/o是绕过传统i/o路径的底层机制,通过createfile、createfilemapping、mapviewoffile三api构成最小完整链路,实现按需加载的虚拟内存块访问,但需严格匹配权限、对齐页边界、检查错误并正确释放资源。

内存映射 I/O 不是“更快的 fread”,而是绕过传统 I/O 路径的底层机制——只要文件能被映射,你就拥有了一个可直接用指针访问的、操作系统按需加载的虚拟内存块。它对超大文件有效,但不是无脑套用就能赢。
Windows 下必须配齐的三个 API:CreateFile + CreateFileMapping + MapViewOfFile
这三个函数构成 Windows 内存映射的最小完整链路,缺一不可,且顺序不能乱:
-
CreateFile必须用GENERIC_READ(只读)或GENERIC_READ | GENERIC_WRITE(读写),并传入FILE_SHARE_READ(甚至FILE_SHARE_WRITE,如果允许多进程同时写) -
CreateFileMapping的flProtect参数要和MapViewOfFile的dwDesiredAccess匹配:比如用PAGE_READONLY就得配FILE_MAP_READ;用PAGE_READWRITE就得配FILE_MAP_ALL_ACCESS -
MapViewOfFile的dwFileOffsetLow和dwFileOffsetHigh必须是系统页面大小(通常 4096)的整数倍,否则映射失败且返回NULL - 错误检查不能只看返回值:调用失败后务必用
GetLastError()查具体原因,常见如ERROR_MAPPED_FILE(文件已被映射)、ERROR_NOT_ENOUGH_MEMORY(虚拟地址空间碎片化严重)
Linux 下 mmap 的关键参数与对齐陷阱
mmap 看似简单,但两个细节不处理就会崩溃或性能暴跌:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
len参数必须向上对齐到页边界:内核会自动向下取整,导致末尾数据不可见。正确做法是:size_t len = (file_size + getpagesize() - 1) & ~(getpagesize() - 1); -
prot和flags组合有隐含约束:MAP_PRIVATE配PROT_READ安全;但若想写后持久化,必须用MAP_SHARED+msync(MS_SYNC),否则修改只存在于映射副本中 -
fd在mmap成功后可以立刻close()—— 映射不依赖 fd 存活,这点和 Windows 不同,但很多人误以为要一直保持打开 - 映射失败返回
MAP_FAILED(即(void*)-1),不是NULL,用== NULL判断会漏掉错误
跨平台大文件映射的通用避坑点
无论 Windows 还是 Linux,以下问题在 TB 级文件上会集中爆发:
- 不要一次性映射整个文件:32 位进程虚拟地址空间有限(约 2GB 可用),64 位虽宽裕但映射 TB 文件仍可能触发
ENOMEM。改用分段映射:MapViewOfFile指定偏移和长度,或mmap的offset参数滑动窗口 - 指针访问前必须确认地址合法:映射区域外的访问(如
pData[file_size])会触发ACCESS_VIOLATION(Windows)或SEGV(Linux),没有边界检查 - 多线程并发读没问题,但写必须加同步:即使只读映射,多个线程同时
MapViewOfFile同一文件也没问题;但写时若没用Interlocked或std::atomic保护,或没配MAP_SHARED+msync,数据一致性无法保证 - 资源释放顺序固定:
UnmapViewOfFile→CloseHandle(hMapping)→CloseHandle(hFile)(Windows);munmap→close(fd)(Linux)。顺序错或漏掉任意一步,都可能导致文件被锁死或句柄泄漏
最常被忽略的不是语法,而是“映射成功≠数据已加载”——首次访问某页时触发页面错误,由 OS 同步从磁盘加载,这个延迟不可控。如果你的逻辑依赖“映射后立即可用”,就得提前用 VirtualAlloc + MEM_COMMIT(Windows)或 mlock(Linux)预热,但这会吃掉物理内存,得权衡。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










