2.3 内存映射 I/O (mmap)
2.3 内存映射 I/O (mmap)
1 概念
内存映射 mmap 则是从完全不同的角度处理文件读写:将整个文件透明地映射成一段内存,像操作指针一样去读写这块内存区域。
首先使用 mmap
映射打开的文件,随后可以像操作内存一样,直接使用 strncpy
之类的操作。最后使用 munmap
解除映射并清理资源。
2 Code Snippet
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
int main() {
const char* filepath = "mmap_example.txt";
const size_t filesize = 4096; // 4KB文件大小
// 1. 创建并打开文件
int fd = open(filepath, O_RDWR | O_CREAT, (mode_t)0600);
if (fd == -1) {
perror("Error opening file for writing");
return 1;
}
// 2. 调整文件大小
if (lseek(fd, filesize-1, SEEK_SET) == -1) {
close(fd);
perror("Error calling lseek() to stretch the file");
return 1;
}
if (write(fd, "", 1) == -1) {
close(fd);
perror("Error writing last byte of the file");
return 1;
}
// 3. 将文件映射到内存
char* map = (char*)mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
return 1;
}
// 4. 写入数据到内存映射区
const char* text = "Hello, mmap world!";
strncpy(map, text, strlen(text));
// 5. 从内存映射区读取数据
std::cout << "Read from mmap: " << map << std::endl;
// 6. 清理
if (munmap(map, filesize) == -1) {
close(fd);
perror("Error un-mmapping the file");
return 1;
}
close(fd);
return 0;
}
运行一下
➜ snip git:(master) ✗ g++ -Wall -Wextra -g -o 03 ./03_mmap.cpp
➜ snip git:(master) ✗ ./03
Read from mmap: Hello, mmap world!
3 优势和局限性
mmap
数据直接从磁盘映射到用户空间,避免了内核缓冲区到用户缓冲区的拷贝。
还有一种用得比较多的方式是作为进程间通信使用,直接共享内存交换数据。
笔者很少见到使用 mmap
作为读写数据引擎的基本方式。在构建存储引擎时,有开发者批评 mmap
的劣势在于不能完全掌控背后内核的内存管理和刷盘机制1。
开发者如果使用 mmap
,尤其是文件远远大于机器内存的情况下需要调优。RocksDB 曾遇到一个未设置 fadvise
导致性能下降的 issue2。Rocksdb 手册3 提到如果数据在内存 fs 中,开启 mmap
会带来比较大的性能提升,否则应该谨慎试用 mmap
选项。