mmap 通常比 fread/fwrite 更快,适用于大于几 mb 的只读或顺序读写;但小文件、随机写或多线程频繁修改时更慢甚至出错,因其依赖虚拟内存映射、需注意打开标志、保护参数匹配、同步落盘及跨平台差异。

Linux 下用 mmap 替代 fread/fwrite 读写大文件
直接结论:对大于几 MB 的只读或顺序读写场景,mmap 通常比传统 I/O 快;但小文件、随机写、多线程频繁修改同一区域时反而更慢甚至出错。
核心原因是 mmap 把文件“映射”进进程虚拟地址空间,后续读写变成内存操作,省去内核态/用户态拷贝和系统调用开销。但它不自动同步磁盘,也不保证原子性。
- 必须用
open()获取文件描述符,且flags要匹配:只读用O_RDONLY,读写用O_RDWR(O_WRONLY不行) -
mmap()的prot参数必须与打开方式一致:只读文件只能传PROT_READ,否则返回MAP_FAILED - 映射后不能
close(fd)—— Linux 允许,但 POSIX 不保证行为;Windows 的CreateFileMapping则要求句柄保持打开 - 写入后需调用
msync(addr, length, MS_SYNC)才能确保落盘;不调用可能在进程退出时才刷,或被系统回收时丢数据
mmap 随机读取大日志文件的典型写法
比如解析一个 2GB 的文本日志,按行跳转查找某时间戳,用 fseek+fgets 每次都要寻道+拷贝缓冲区;而 mmap 可以直接指针运算定位:
int fd = open("access.log", O_RDONLY);
struct stat st;
fstat(fd, &st);
char *data = (char*)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
// 现在 data[i] 就是第 i 字节,可直接用 memchr 找换行符、strstr 找关键字
// 注意:data[st.st_size] 是非法地址,别越界
-
MAP_PRIVATE对只读场景最安全:写操作会触发 COW,不影响原文件,也避免msync开销 - 不要用
MAP_SHARED | PROT_WRITE去改日志文件——并发写易破坏结构,且没锁保护 - 如果文件可能被其他进程截断,访问已映射但被删的区域会触发
SIGBUS,得用sigaction捕获处理
Windows 上等效方案不是 mmap,而是 CreateFileMapping + MapViewOfFile
C++ 跨平台代码里硬写 mmap 在 Windows 会编译失败。MinGW 支持但行为不完全兼容;MSVC 完全不提供。
- Windows 要先用
CreateFile(注意GENERIC_READ/GENERIC_WRITE权限)拿到句柄 -
CreateFileMapping的flProtect必须和文件打开权限一致,例如只读文件只能用PAGE_READONLY -
MapViewOfFile返回的指针,释放必须配对调用UnmapViewOfFile+CloseHandle,漏掉任一都会泄漏句柄或内存 - Windows 不支持匿名映射(
INVALID_HANDLE_VALUE),也不能映射设备文件,灵活性低于 Linux
容易被忽略的三个坑
很多教程只贴成功代码,但线上崩就崩在这三处:
- 映射长度传
0或负数 →mmap返回MAP_FAILED,但新手常忘记检查,直接解引用崩溃 - 文件被其他进程 truncate,Linux 下继续读映射区会收到
SIGBUS;glibc 默认终止进程,必须提前注册信号处理器 -
mmap占用的是进程虚拟内存(VMA),不是物理内存;映射 10GB 文件不会立刻吃掉 10GB 物理内存,但ulimit -v限制虚拟内存总量时可能失败
真正要用好,得盯住 /proc/[pid]/maps 看映射是否成功、是否被 split,而不是只看 return value。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











