在 Linux 中,内存映射 是一种将文件或设备的内容直接映射到进程虚拟地址空间的机制,使进程能像操作普通内存一样访问文件或共享内存,核心由系统调用 mmap 实现,涉及虚拟内存管理、缺页异常处理等底层机制。以下是其关键知识点的详细解析:
9.1内存映射的分类与概念
-
文件映射 将磁盘文件的一段区间映射到进程虚拟地址空间,数据源为文件。例如数据库、大文件的随机读写场景,可避免频繁的
read/write系统调用,提升性能。 -
匿名映射无文件支持的内存映射,将物理内存直接映射到虚拟地址空间(如进程堆、栈或共享内存)。典型场景包括进程内大块内存分配、父子进程共享内存等。
-
共享映射与私有映射
- 共享映射:多个进程映射同一区域时,修改会同步到文件或其他进程。
- 私有映射:修改仅在当前进程的内存副本中生效,不影响文件或其他进程。
9.2核心系统调用:mmap 与 munmap
1. mmap 原型与参数
cpp
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:期望的映射起始地址(通常设为NULL,由系统自动分配)。length:映射区域的大小(需页对齐,通常为 4KB 的整数倍)。prot:内存保护权限(PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE组合)。flags:映射类型(如MAP_SHARED、MAP_PRIVATE、MAP_ANONYMOUS)。fd:文件描述符(匿名映射时为-1)。offset:文件内的偏移量(需页对齐)。
2. munmap 用于解除映射·
cpp
int munmap(void *addr, size_t length);
9.3底层实现机制
-
虚拟内存区域(
vm_area_struct) 映射时,内核在进程虚拟地址空间中创建一个vm_area_struct结构体,描述该映射区域的起始 / 结束地址、权限、映射类型等信息。 -
延迟分配与缺页异常 内核采用 "延迟分配" 策略:仅在进程首次访问 映射区域时,触发缺页异常,再分配物理内存并建立页表映射:
- 文件映射:从文件读取数据到物理页,更新页表。
- 匿名映射:直接分配物理页,初始化后更新页表。
-
页表与地址翻译通过多级页表(如 PGD→PUD→PMD→PTE)管理虚拟地址到物理地址的映射。MMU(内存管理单元)在访问时自动解析页表,若页表项不存在则触发缺页异常,由内核处理。
9.4典型应用场景
-
高效文件操作 示例:将文件映射到内存后直接修改,无需
read/write:cpp#include <fcntl.h> #include <sys/mman.h> int fd = open("test.txt", O_RDWR); char *map = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); strcpy(map, "直接修改文件内容"); munmap(map, 4096); close(fd); -
进程间共享内存两个进程映射同一文件的共享区域,实现数据共享:
cpp// 进程A int fd = open("shared.dat", O_CREAT | O_RDWR, 0666); ftruncate(fd, 4096); char *shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); strcpy(shm, "共享数据"); // 进程B int fd = open("shared.dat", O_RDWR); char *shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); printf("%s\n", shm); // 输出"共享数据" -
匿名内存分配 分配不关联文件的内存区域(类似
malloc,但由内核直接管理):cppvoid *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); strcpy((char*)ptr, "匿名映射内存"); munmap(ptr, 4096);