mmap比fread更快读大文件,因其将文件直接映射为进程虚拟内存,读取即内存访问,省去内核/用户态拷贝与系统调用开销,尤其利于随机读、重复读和多线程并发读。

为什么 mmap 比 fread 更快读大文件
mmap 不走传统 I/O 缓冲路径,它把文件直接映射成进程虚拟内存的一段,后续读取就是普通的内存访问(mov 指令级别),省去了内核态/用户态多次拷贝、系统调用开销。尤其对随机读、重复读、多线程并发读大文件,优势明显。但注意:它不减少磁盘 IO 本身,只是优化了数据搬运路径。
常见误判点:mmap 并非“一定更快”——小文件(
Linux 下 mmap 读取大文件的最小可靠写法
核心是正确处理 mmap 返回地址、长度对齐、以及 MAP_PRIVATE / MAP_SHARED 的选择。对只读场景,优先用 MAP_PRIVATE,避免写时复制(COW)干扰。
-
open()必须带O_RDONLY,且检查返回值是否为 -1 -
lseek()或fstat()获取真实文件大小,不能依赖stat.st_size未刷新的情况 -
mmap()的length参数必须 ≤ 文件大小;若传入 > 文件大小,映射区域末尾读取会触发SIGBUS - 映射后记得
munmap(),否则泄漏虚拟内存(虽不占物理内存,但耗vm.max_map_count)
示例关键片段:
立即学习“C++免费学习笔记(深入)”;
int fd = open("large.bin", O_RDONLY);
struct stat sb;
fstat(fd, &sb);
void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) { /* handle error */ }
// 使用 addr 作为 char* 直接读取
munmap(addr, sb.st_size);
close(fd);
mmap 在 Windows 上怎么对应实现
Windows 没有 mmap,但等效机制是 CreateFileMapping() + MapViewOfFile()。行为逻辑一致,但细节差异多:
- 必须先用
CreateFile()打开文件,且dwDesiredAccess要匹配映射保护标志(如只读映射需GENERIC_READ) -
CreateFileMapping()的flProtect用PAGE_READONLY,而非PROT_READ -
MapViewOfFile()返回的是LPCVOID,类型安全起见建议static_cast转换 - 关闭顺序固定:先
UnmapViewOfFile(),再CloseHandle()映射句柄和文件句柄
跨平台封装时,别试图宏定义统一接口——二者错误码、生命周期、权限模型差异太大,容易埋坑。
容易被忽略的性能陷阱和调试信号
真正影响 mmap 实际性能的,往往不是映射本身,而是使用方式:
- 频繁跨页访问(如每次只读 1 字节且地址不连续)会引发大量缺页中断,比
fread更慢 - 未对齐访问(如
int*强转到非 4 字节对齐地址)在某些 CPU 架构上会崩溃或严重降速 -
strace下看不到read()系统调用,但能看到mmap和大量gettimeofday(因缺页统计),这是正常现象 - 用
/proc/[pid]/maps可确认映射是否成功、是否被 split 或 merge,避免误判“没映射上”
调试 SIGBUS 错误,90% 是因为读越界(超过 st_size)或文件被其他进程截断——务必检查文件是否被动态修改。











