mmap读大文件比fread快得多,因其绕过内核缓冲区拷贝,将文件直接映射到进程虚拟内存,实现纯内存操作,避免系统调用与态切换开销,在GB级数据场景下性能提升2–5倍。

为什么 mmap 读大文件比 fread 快得多
mmap 的核心优势在于绕过内核缓冲区拷贝:它把文件直接映射进进程虚拟内存,后续读写变成纯内存操作,没有 read()/write() 系统调用开销,也不触发用户态/内核态频繁切换。对 GB 级日志、数据库页、影像数据等顺序或随机访问场景,性能提升常达 2–5 倍。
但注意:mmap 不是万能加速器——小文件(msync(),否则脏页由内核异步刷盘,崩溃可能丢数据。
Linux 下用 mmap 读取只读大文件的最小可靠写法
关键不是“怎么映射”,而是“怎么避免 SIGBUS 和段错误”。常见错误是忽略文件大小与映射长度对齐、未检查 mmap() 返回值、忘记 munmap()。
-
open()必须带O_RDONLY(读)或O_RDWR(读写),不能用O_APPEND -
lseek()+lstat()获取真实文件大小,传给mmap()的length参数;不能硬写 2GB 或用SIZE_MAX -
mmap()成功返回非MAP_FAILED指针后,**必须用memset()或首次访问前 touch 一页**,防止首次读触发缺页异常被信号中断(尤其在多线程环境) - 映射后按需用
memcpy()或指针偏移访问,无需循环read()
int fd = open("data.bin", O_RDONLY);
struct stat st;
fstat(fd, &st);
void* addr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) { /* handle error */ }
// 安全起见,触碰第一页
volatile char dummy = *(char*)addr;
// … 使用 addr 作只读访问
munmap(addr, st.st_size);
close(fd);
写入大文件时,MAP_SHARED + msync 的正确节奏
写操作比读敏感得多:用 MAP_PRIVATE 写的是 copy-on-write 页,不会落盘;必须用 MAP_SHARED 才能同步回文件。但直接狂写再 msync() 会阻塞太久,影响响应。
立即学习“C++免费学习笔记(深入)”;
- 写前确保文件已用
ftruncate()扩容到目标大小,否则映射区域超出文件尾部会触发SIGBUS -
msync()推荐分块调用:msync(addr + offset, len, MS_SYNC),每 16–64MB 同步一次,平衡延迟与可靠性 - 若允许丢失最后几秒数据,可用
MS_ASYNC异步刷,但程序退出前仍需一次MS_SYNC - 不要在信号处理函数里调用
msync()—— 它不是 async-signal-safe 函数
Windows 上等效方案是 CreateFileMapping,但行为差异点必须知道
Windows 没有 mmap,对应 API 是 CreateFileMapping() + MapViewOfFile()。表面相似,但几个关键区别直接影响性能和健壮性:
- 映射视图(view)大小受
MapViewOfFile()的dwNumberOfBytesToMap限制,**不能超过 4GB 单次映射**(即使文件更大),需分段映射 - Windows 默认不支持 lazy commit,首次写入即分配物理内存,容易 OOM;需用
SEC_COMMIT标志显式控制 - 没有类似
madvise()的提示接口,无法告知系统访问模式(如MADV_SEQUENTIAL),预读优化弱于 Linux - 关闭映射必须严格配对:
UnmapViewOfFile()→CloseHandle(),漏掉任一环节会导致句柄泄漏或文件锁死
跨平台项目建议封装一层抽象,把 “打开-映射-同步-释放” 流程统一,避免在业务逻辑里混用 mmap 和 CreateFileMapping。
真正难的不是调用 API,而是理解页对齐、缺页处理、脏页生命周期这些底层契约——写错一行 ftruncate() 或漏一次 msync(),就可能在线上吃满 CPU 还读出脏数据。











