mmap 比 fread + strstr 更快,因其将文件直接映射至虚拟内存,避免内核与用户空间间的数据拷贝;而 fread 需频繁系统调用和内存复制,但小文件等场景不适用 mmap。

为什么 mmap 比 fread + strstr 更快?
因为 mmap 把文件直接映射进虚拟内存,跳过了内核到用户空间的数据拷贝;而 fread 每次读块都要触发系统调用和内存复制。但前提是:你得用对——不是所有场景都适合 mmap,比如小文件(
常见错误现象:Segmentation fault(没检查 mmap 返回值是否为 MAP_FAILED)、搜索结果偏移错乱(忘了减去 mmap 起始地址)、匹配不到换行符附近的模式(没注意文件末尾是否被截断或映射长度计算错误)。
- 使用场景:日志分析、代码库全文索引、只读大文本(GB 级)的单次批量搜索
- 映射时务必用
PROT_READ+MAP_PRIVATE,写保护能避免误改导致崩溃 - 文件大小需用
stat()获取,不能依赖lseek(fd, 0, SEEK_END)后的ftell,某些文件系统(如 procfs)不支持
如何安全地在 mmap 区域里做子串搜索?
别直接拿 strstr 往 mmap 返回的指针上砸——它不保证越界安全,且遇到中间的 \0 就停(二进制文件或含 null 字节的 UTF-16 文本会崩)。正确做法是用带长度限制的搜索逻辑。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
memmem(GNU 扩展)或手写memcmp循环,显式传入映射长度mapped_size - 搜索前确认 pattern 不为空,且长度 ≤
mapped_size,否则memmem行为未定义 - 如果 pattern 可能含
\0(比如搜二进制特征码),必须用memmem,不能用strstr - 避免在循环里反复调用
memmem搜索重叠匹配——每次应从上一匹配位置 + 1 开始,而不是 + pattern_len
示例关键片段:
const char* found = (const char*)memmem(mapped_addr, mapped_size, pattern, pattern_len);
mmap 搜索在不同平台上的兼容性坑
mmap 在 Linux/macOS 行为接近,但 Windows 完全不认这个 API——得用 CreateFileMapping + MapViewOfFile。如果你写跨平台代码,别硬塞 POSIX 头,先抽象一层。
容易踩的坑:
- Linux 上
mmap映射空文件会成功但返回全零页,macOS 则直接失败;测试时务必用真实非空文件 - Android NDK 默认关闭
MAP_POPULATE,缺页中断延迟明显,加了又可能卡主线程——权衡后建议只对热数据段预取 - 32 位程序 mmap 超过 2GB 文件大概率失败(地址空间不足),64 位下也建议单次映射 ≤ 128MB,避免
ENOMEM
搜索结果怎么转成文件内真实行号?
拿到 memmem 返回的地址差值后,不能直接除以平均行长——文本行长度千差万别。必须从映射起点开始逐字节扫 \n,但别傻乎乎从头扫:用二分定位大致区间再线性回溯,性能好得多。
实操要点:
- 记录每个
\n的偏移(构建稀疏索引),适合多次搜索同一文件 - 若只搜一次,就从匹配地址往前找最近的
\n(最多 4KB 回溯足够,一行超这长度的文本极少) - 注意 CRLF(
\r\n):Windows 文本里\n前必有\r,但搜索时 pattern 通常不含\r,所以行号计算要按逻辑换行符算,不是物理字节
最常被忽略的一点:mmap 地址空间和文件 offset 是一一对应的,但如果你用了 MAP_SHARED 且文件被其他进程截断,mapped_size 还是旧的,访问越界区域会触发 SIGBUS——永远用 min(actual_file_size, mapped_size) 作边界。



















