mmap比fread快是因为将文件直接映射为进程虚拟内存,读写即内存访问,省去内核/用户态数据拷贝和系统调用开销,尤其适合随机读、重复读和多线程并发读;但不减少磁盘io本身,且仅适用于文件大小稳定、需频繁访问的场景。

Linux下用mmap读写大文件为什么比fread快?
因为mmap把文件直接映射进进程虚拟地址空间,后续读写变成内存访问,省去内核态/用户态反复拷贝。但前提是文件大小稳定、随机访问多、且你控制好映射范围——不是所有场景都适用。
常见错误是映射后直接用memcpy越界写,或忘记调用msync同步脏页,导致程序退出后数据丢失。
- 必须用
open以O_RDWR打开文件,只读映射无法触发写回 -
mmap的length参数不能超过文件当前大小(lseek + write先扩展再映射) - 若需并发读写,需额外加锁;
mmap本身不提供同步机制 - 小文件(mmap反而可能更慢,因TLB压力和映射开销
int fd = open("data.bin", O_RDWR);
ftruncate(fd, 1024 * 1024); // 先扩大小
void* addr = mmap(nullptr, 1024 * 1024, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
// 写入:*(int*)addr = 42;
// 同步:msync(addr, 1024 * 1024, MS_SYNC);
munmap(addr, 1024 * 1024);
close(fd);
Windows下CreateFileMapping映射失败的典型原因
最常卡在INVALID_HANDLE_VALUE或GetLastError()返回ERROR_INVALID_PARAMETER——往往不是API用错,而是前置条件没满足。
关键点在于:文件句柄必须是用GENERIC_READ | GENERIC_WRITE打开,且CreateFileMapping的flProtect要和MapViewOfFile的dwDesiredAccess匹配。
- 映射大小不能为0,且
dwMaximumSizeHigh/dwMaximumSizeLow需与文件实际大小一致(或更大,但文件得先SetFilePointerEx扩展) - 命名映射(如
L"SharedMem")需注意跨进程权限,本地测试建议用INVALID_HANDLE_VALUE配合普通文件句柄 -
MapViewOfFile返回NULL时,90%是因为dwNumberOfBytesToMap超出映射对象尺寸
HANDLE hFile = CreateFile(L"data.bin", GENERIC_READ | GENERIC_WRITE,
0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
SetFilePointerEx(hFile, {1024 * 1024, 0}, nullptr, FILE_BEGIN);
SetEndOfFile(hFile);
HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, 1024 * 1024, nullptr);
LPVOID addr = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 1024 * 1024);
// 写入:*(int*)addr = 42;
FlushViewOfFile(addr, 1024 * 1024);
UnmapViewOfFile(addr);
CloseHandle(hMap);
CloseHandle(hFile);
mmap和CreateFileMapping在跨平台封装时要注意什么?
核心差异不在API形态,而在语义边界:Linux的mmap允许映射未完全写入的文件区域(缺页时按需加载),Windows的CreateFileMapping要求映射尺寸≤文件尺寸,否则MapViewOfFile失败。
如果你写一个通用类,别试图“抹平”行为,而是暴露两个关键控制点:是否预分配文件空间、是否启用延迟加载。
- 统一用
size_t表示映射长度,避免off_t和DWORD混用导致截断 - Linux下
MAP_POPULATE可预加载页面减少缺页中断,Windows无等价选项,需自行memset触发 - 销毁时Linux调
munmap+close,Windows必须按UnmapViewOfFile→CloseHandle(hMap)→CloseHandle(hFile)顺序,漏一步就泄漏
为什么修改映射内存后程序崩溃,但错误不在mmap调用处?
因为段错误(SIGSEGV)或Windows的ACCESS_VIOLATION通常发生在你第一次访问非法地址时,而不是映射那一刻。比如映射了1MB,却写了第1025KB位置,崩溃点在那行ptr[1048576] = x,不是mmap返回值检查处。
调试这类问题,优先看mmap返回地址是否为MAP_FAILED,再确认stat或GetFileSize得到的文件大小是否≥映射长度。工具上,Linux用/proc/self/maps查映射区间,Windows用Process Explorer看Memory Regions。
- 用
valgrind --tool=memcheck能捕获越界写,但对mmap区域支持有限;更可靠的是编译时加-fsanitize=address - Windows下开启Application Verifier的Heaps选项,能提前捕获映射区外写
- 永远不要假设映射成功后指针非空——
mmap失败返回MAP_FAILED(即(void*)-1),不是nullptr
跨平台内存映射真正难的不是调用API,而是对齐文件大小、控制映射粒度、处理异步写回时机——这些细节一旦出错,表现就是数据静默损坏,而非立即崩溃。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











