mmap概念
mmp是 将文件或设备直接映射到进程的虚拟内存空间 的一种机制,可实现程序像访问内存一样访问文件,而不需要传统的 read()/write()系统调用
文件内容被映射到进程的地址空间,读写文件就像操作内存一样,操作系统负责自动同步内存和文件的修改(除非显式禁用)。
好处:
- 文件 I/O 优化:避免频繁的 read()/write(),提升性能,例如数据库引擎(如 SQLite、LevelDB)用 mmap加速磁盘访问
- 多个进程需要高效共享数据(如父子进程、独立进程),比管道、消息队列更快(直接内存访问),Chrome 浏览器用 mmap共享多个标签页的内存,机器学习训练时,多个进程共享模型参数
- 序运行时加载动态链接库(.so/.dll)时,库文件被映射到内存,按需加载(懒加载),节省内存
- sendfile:文件通过 mmap映射到内存,直接通过 DMA 发送到网卡,无需 CPU 参与拷贝
语法:函数声明
c
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数 | 说明 |
---|---|
addr |
建议映射的起始地址(通常传 NULL ,由系统自动分配) |
length |
映射区域的长度(字节) |
prot |
内存保护方式(PROT_READ , PROT_WRITE 等) |
flags |
映射类型(MAP_SHARED , MAP_PRIVATE ) |
fd |
文件描述符(通过 open 获得) |
offset |
文件映射起始偏移(必须是页大小的整数倍) |
相关常量:
c
// 保护方式
PROT_READ // 可读
PROT_WRITE // 可写
PROT_EXEC // 可执行
PROT_NONE // 不可访问
// 映射类型
MAP_SHARED // 修改会写回文件,进程间共享
MAP_PRIVATE // 写时复制,修改不写回文件
// 取消映射
int munmap(void *addr, size_t length); // 释放映射
mmap写文件
c
[root@prs31 01-fd]# cat 01-mmap_read.c
// 01-mmap_read.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
int main() {
int fd;
struct stat sb;
char *mapped;
fd = open("test.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
// 2. 获取文件状态(主要是大小)
if (fstat(fd, &sb) == -1) {
perror("fstat");
exit(1);
}
// 3. 映射文件到内存
mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
close(fd);
exit(1);
}
// 4. 像访问数组一样读取内容
printf("文件内容:\n");
for (off_t i = 0; i < sb.st_size; i++) {
putchar(mapped[i]); // 逐字符打印内存映射文件(mmap)的内容
}
// 5. 解除映射
if (munmap(mapped, sb.st_size) == -1) {
perror("munmap");
}
close(fd);
return 0;
}

mmap追加文件内容
c
[root@prs31 01-fd]# cat 02-mmap_write.c
// mmap_write.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
int main() {
int fd;
struct stat sb;
char *mapped;
const char *new_content = "\nAppended via mmap!";
// 1. 以读写方式打开文件
fd = open("test.txt", O_RDWR);
if (fd == -1) {
perror("open");
exit(1);
}
// 2. 获取文件大小
if (fstat(fd, &sb) == -1) {
perror("fstat");
exit(1);
}
// 3. 扩展文件大小(为追加内容预留空间)
off_t new_size = sb.st_size + strlen(new_content);
if (ftruncate(fd, new_size) == -1) {
perror("ftruncate");
close(fd);
exit(1);
}
// 4. 映射整个新大小的文件,这里传递使用扩展后的文件大小
mapped = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
close(fd);
exit(1);
}
// 5. 在原内容末尾写入新内容,strcpy(目的变量,要添加的变量)
strcpy(mapped + sb.st_size, new_content);
printf("已追加内容到文件\n");
if (munmap(mapped, new_size) == -1) {
perror("munmap");
}
close(fd);
return 0;
}

mmap实现进程间共享内存
c
[root@prs31 01-fd]# cat 03-shm_writer.c
// 03-shm_writer.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
int main() {
int fd;
char *mapped;
// 1. 创建共享文件
fd = open("/tmp/shm_file", O_CREAT | O_RDWR, 0644);
if (fd == -1) {
perror("open");
exit(1);
}
// 2. 扩展文件大小
ftruncate(fd, 4096); // 1页
// 3. 映射
mapped = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
exit(1);
}
// 4. 写入数据
sprintf(mapped, "Hello from Process A (PID: %d)", getpid());
printf("写入: %s\n", mapped);
printf("等待按回车继续...\n");
getchar(); // 等待进程B读取
munmap(mapped, 4096);
close(fd);
return 0;
}
c
[root@prs31 01-fd]# cat 03-shm_reader.c
// 03-shm_writer
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
int main() {
int fd;
char *mapped;
fd = open("/tmp/shm_file", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
mapped = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
exit(1);
}
printf("读取到: %s\n", mapped);
munmap(mapped, 4096);
close(fd);
return 0;
}
运行代码
shell
[root@prs31 01-fd]# gcc 03-shm_writer.c shm_writer
[root@prs31 01-fd]# gcc 03-shm_reader.c shm_reader

第二个终端: