必须分块映射超大文件:Windows需x64编译+64KB对齐+UnmapViewOfFile释放,Linux用MAP_PRIVATE+页对齐+munmap,均应避免单次映射4GB以上,推荐64MB块大小并显式传入st_size。

Windows下用CreateFileMappingA映射超大文件必须设为64位模式
32位程序的用户态地址空间只有2GB(默认)或3GB(/LARGEADDRESSAWARE),根本无法容纳4GB以上文件的一次性映射。哪怕你调用CreateFileMappingA成功,MapViewOfFile也会在尝试映射超过2GB偏移时返回NULL,且GetLastError()返回ERROR_INVALID_PARAMETER——这不是权限问题,是地址空间硬限制。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 项目属性中必须启用
/LARGEADDRESSAWARE(仅缓解,不解决) - 真正可靠的做法是:编译为x64目标,使用
size_t和LARGE_INTEGER处理文件大小,所有偏移量用ULARGE_INTEGER或uint64_t传入 - 打开文件时务必用
GENERIC_READ+FILE_FLAG_NO_BUFFERING(可选,减少内核缓存干扰) -
CreateFileMappingA的dwMaximumSizeHigh/dwMaximumSizeLow必须拆分高位/低位32位,不能只填dwMaximumSizeLow
Linux下mmap需要PROT_READ | MAP_PRIVATE且避免MAP_POPULATE
mmap对超大文件最常踩的坑是误用MAP_SHARED:它会让内核把修改同步回磁盘,不仅没必要,还可能触发写保护异常(尤其只读场景);而MAP_POPULATE看似“预加载”,实则会阻塞直到全部页完成缺页中断,在4GB+文件上可能卡死几十秒甚至OOM kill。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 只读场景固定用
PROT_READ | MAP_PRIVATE,既安全又省内存 - 文件描述符必须用
O_RDONLY | O_DIRECT(可选,绕过page cache,但需对齐512字节) - 映射长度不要传
0——mmap会截断到SIZE_MAX,导致ENOMEM;显式传st_size(用stat()获取) - 检查返回值是否为
MAP_FAILED,而非NULL
跨平台分块映射比单次映射更稳
即使64位系统,一次性映射4GB+文件也可能失败:Windows有commit charge限制,Linux受vm.max_map_count和可用虚拟内存影响。更健壮的做法是按64MB或128MB切块,每次只映射当前需要访问的区域。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 定义块大小如
const size_t CHUNK_SIZE = 67108864;(64MB) - 用
uint64_t offset计算当前块起始位置,确保offset % CHUNK_SIZE == 0 - 每次
MapViewOfFile或mmap前先UnmapViewOfFile或munmap前一块(避免句柄/VM耗尽) - 对齐要求:Windows要求映射起始偏移必须是
GetSystemInfo().dwAllocationGranularity(通常64KB),Linux要求页对齐(getpagesize(),通常4KB)
读取时直接指针运算,别用std::ifstream或fread
内存映射的意义在于绕过libc缓冲和系统调用开销。一旦用了std::ifstream::seekg或fread,就退化成普通IO,还多占一份映射内存。正确做法是把映射返回的指针当普通数组用。
实操示例(Windows):
auto ptr = static_cast<const uint8_t*>(mapped_view); // 直接按偏移读:ptr[logical_offset],或用memcpy拷贝一段 memcpy(buffer, ptr + offset_in_file, length);
注意事项:
- 逻辑偏移
offset_in_file必须小于文件实际大小,否则越界访问触发ACCESS_VIOLATION(Windows)或SEGV_MAPERR(Linux) - 不要对映射区域调用
delete或free——必须配对用UnmapViewOfFile/munmap - 如果文件被其他进程截断,后续访问可能引发信号/异常,需用
SetUnhandledExceptionFilter或sigaction捕获
MapViewOfFile返回NULL却查不出原因。


















