C++如何处理大文件读取?(内存映射mmap用法)

2次阅读

mmap 比 fread 适合读大文件,因其按需映射、避免大块内存分配和冗余拷贝,尤其适用于只读或稀疏访问的 gb 级文件;但全量顺序读时可能因缺页中断变慢,此时 read + 预读更优。

C++如何处理大文件读取?(内存映射mmap用法)

为什么 mmapfread 适合读大文件

因为 mmap 不把整个文件一次性拷贝进用户内存,而是让内核按需把磁盘页映射到进程地址空间。你访问哪段,内核才加载哪段,避免了 malloc + fread 的大块内存分配和冗余拷贝。尤其对 GB 级日志、数据库快照、二进制数据集这类只读或稀疏访问的场景,延迟更低、系统开销更小。

但注意:mmap 不是万能加速器——如果文件远大于物理内存,且你顺序扫全量内容,mmap 反而可能因频繁缺页中断拖慢速度;这时候 read(2) 配合预读(posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED))更稳。

mmap 最简安全用法(linux / macos

核心就三步:打开文件 → 获取大小 → 映射。关键不是“怎么写”,而是“怎么避错”:

  • open() 必须带 O_RDONLY(只读)或 O_RDWR(可写),不能用 O_APPENDO_TRUNC
  • 映射前务必用 fstat() 拿真实文件大小,别信 lseek(fd, 0, SEEK_END) —— 对某些特殊文件(如设备节点、/proc 下文件)会失败或返回 0
  • mmap() 返回 MAP_FAILED 时,必须检查 errnoEINVAL(offset 未按页对齐)、ENOMEM(虚拟地址空间不足,不是内存不够)、EPERM(文件不可执行且 noexec 挂载选项启用)
  • 映射后记得 munmap(),否则泄漏的是虚拟内存(不是物理内存),但会耗尽进程地址空间(尤其在 32 位或受限容器里)

示例片段:

立即学习C++免费学习笔记(深入)”;

int fd = open("data.bin", O_RDONLY); Struct stat st; fstat(fd, &st); void* ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (ptr == MAP_FAILED) { /* 处理 errno */ } // 使用 ptr 当作普通指针读取 munmap(ptr, st.st_size); close(fd);

windows 上没 mmap 怎么办

Windows 没原生 mmap,但有语义等价的 CreateFileMapping + MapViewOfFile。别硬套 Linux 写法,重点差异在:

  • 文件句柄必须用 GENERIC_READ 打开,且 CreateFileMappingflProtect 要匹配:只读用 PAGE_READONLY,可写用 PAGE_READWRITE
  • 映射大小不能超过文件实际长度,且 MapViewOfFiledwNumberOfBytesToMap 若为 0,表示映射全部(Linux 下 mmapLength 为 0 是非法的)
  • 错误判断靠 GetLastError(),常见 ERROR_MAPPED_FILE(文件被其他进程以不兼容方式打开)、ERROR_NOT_ENOUGH_MEMORY(不是物理内存不足,而是用户模式地址空间碎片化)

跨平台代码建议封装一层,比如用宏区分:#ifdef _WIN32 走 Windows API,否则走 mmap

映射后访问越界或 SIGBUS 怎么办

SIGBUSmmap 最典型的运行时错误,根本原因是访问了未映射的地址——不是空指针那种崩溃,而是内核直接发信号终止进程。常见原因:

  • 文件在映射后被截断(ftruncate)或删除(unlink),导致后续访问对应页时缺页失败
  • 映射时用了 MAP_SHARED,但文件被其他进程并发修改,破坏了页对齐假设
  • sizeof(struct) 计算结构体数组偏移,但结构体有 padding,跨页边界时部分字段落在未映射区域
  • 忘了 st.st_size 是字节数,却当成“元素个数”去循环访问(比如 for (int i=0; i<size i arr>,但 <code>arruint64_t*

调试技巧:用 gdb 捕获 SIGBUS,然后 info proc mappings 看当前映射范围;生产环境建议加 sigaction 捕获并记录 si_addr(出错地址)与 /proc/self/maps 对比。

真正麻烦的不是映射本身,是文件生命周期和内存访问模式的耦合——一旦文件被外部工具轮转、压缩或清空,mmap 区域就变成定时炸弹。

text=ZqhQzanResources