在现代 C++ 开发中,内存映射文件(memory‑mapped files)提供了一种高效读取大文件或共享数据的方式。与传统的文件 I/O(fread/fwrite、fstream)相比,mmap 能够把文件映射到进程地址空间,使得对文件内容的访问像访问普通内存一样简单,且由操作系统负责缓存与磁盘同步。
1. 为什么要使用 mmap?
- 性能提升:文件内容被映射后,读取不需要系统调用,数据直接在内存中访问,减少了数据拷贝开销。
- 延迟加载:文件被按需加载,只有实际访问的页面才会被调入内存,降低初始加载时间。
- 共享内存:不同进程可以映射同一文件,内存页会被共享,适用于 IPC。
- 大文件支持:对于超过 2GB 的文件,mmap 可以一次性映射整个文件,避免缓冲区大小限制。
2. 关键 API(POSIX 版本)
int open(const char *pathname, int flags, ...); // 打开文件
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset); // 映射文件
int munmap(void *addr, size_t length); // 解除映射
int close(int fd); // 关闭文件
prot:访问权限,如PROT_READ | PROT_WRITE。flags:映射属性,如MAP_SHARED(共享)或MAP_PRIVATE(写时复制)。offset:文件偏移,通常为 0。
3. 简单示例:读取文本文件
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <iostream>
#include <cstring>
int main() {
const char *file = "sample.txt";
// 1. 打开文件
int fd = open(file, O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
// 2. 获取文件大小
struct stat st;
if (fstat(fd, &st) < 0) {
perror("fstat");
close(fd);
return 1;
}
size_t len = st.st_size;
// 3. 映射文件
void *map = mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 4. 处理内容(这里直接输出)
std::cout.write(static_cast<char*>(map), len);
std::cout << std::endl;
// 5. 解除映射并关闭文件
munmap(map, len);
close(fd);
return 0;
}
运行步骤
- 用
g++ -std=c++17 mmap_example.cpp -o mmap_example编译。 - 准备一个
sample.txt,填入内容。 - 运行
./mmap_example即可看到文件内容。
4. 写入操作(MAP_SHARED)
如果想在映射区域修改内容并同步回磁盘,需要使用 MAP_SHARED 并确保 PROT_WRITE:
void *map = mmap(nullptr, len, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
此时,对映射内存的写操作会直接写回文件。请注意:
- 写时复制(
MAP_PRIVATE)不会影响原文件。 - 必须确保映射内存的生命周期与文件描述符保持一致。
5. 注意事项
| 场景 | 说明 |
|---|---|
| 多线程 | 每个线程共享同一映射区域时,要注意同步,避免数据竞争。 |
| 大文件 | mmap 能一次映射整个文件,但如果文件非常大,仍需根据系统内存情况决定是否分段映射。 |
| 关闭文件 | 关闭 fd 后映射仍然有效,但不建议在映射后立即关闭。 |
| 取消映射 | 必须先 munmap 再 close,否则会导致资源泄漏。 |
6. 高级用法:映射一个文件的某个区域
void *map = mmap(nullptr, page_size, PROT_READ,
MAP_PRIVATE, fd, offset);
通过调整 offset 与 length,可以只映射文件的一部分,常用于内存映射数据库、日志文件切片等场景。
7. 小结
内存映射文件是 C++ 开发中提高 I/O 性能的重要手段。掌握 mmap 的基本调用方式与使用场景,能够在处理大文件、实现高性能服务器或跨进程共享数据时,获得显著的性能优势。需要注意的是,尽管 mmap 简化了文件访问,但它也带来了与内存管理相关的细节,如页面错误、同步与一致性问题,使用时应结合具体业务场景慎重选择。