Linux内存映射编程与VMA堆栈管理 - 第1节
1.1 什么是 VMA —— 虚拟内存区域
VMA(Virtual Memory Area,虚拟内存区域)是 Linux 内核中描述进程地址空间一段连续虚拟内存的基本数据结构。每个 VMA 对应一个独立的、具有相同访问权限和映射属性的虚拟地址区间。内核使用 struct vm_area_struct 结构体来管理每一个 VMA。
核心要点:VMA 是进程地址空间管理的“原子单元”,所有用户态内存操作(堆、栈、文件映射、匿名映射)最终都体现为对 VMA 链表的增删改查。
struct vm_area_struct 关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
vm_start | unsigned long | VMA 起始虚拟地址(页对齐) |
vm_end | unsigned long | VMA 结束虚拟地址(页对齐,不包含) |
vm_flags | unsigned long | 访问权限与属性标志(读/写/执行/共享/私有等) |
vm_file | struct file * | 映射的文件对象(匿名映射为 NULL) |
vm_ops | const struct vm_operations_struct * | VMA 操作函数表(open/close/fault 等) |
vm_next | struct vm_area_struct * | 链表指针,指向下一个 VMA |
vm_mm | struct mm_struct * | 所属进程的内存描述符 |
1.2 进程地址空间布局
在 64 位 Linux 系统中,每个用户态进程拥有独立的 128 TB 虚拟地址空间(0x0000000000000000 ~ 0x00007fffffffffff)。内核按照固定布局将地址空间划分为若干区域,每个区域由一个或多个 VMA 构成。
典型 64 位进程地址空间布局
布局规律:代码段位于最低地址,栈位于最高地址。堆和栈相向增长,中间大片区域供 mmap 映射使用。这种布局最大化利用了虚拟地址空间,减少了堆栈碰撞的概率。
1.3 mmap 系统调用概述
mmap 是 Linux 中最核心的内存映射系统调用,它允许用户态程序在进程地址空间中创建新的 VMA,将文件或设备内容映射到内存,或者分配匿名内存页。其原型定义在 <sys/mman.h> 中。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
// 返回值:成功返回映射起始地址,失败返回 MAP_FAILED ((void *) -1)
参数详解
| 参数 | 说明 | 常用值 |
|---|---|---|
addr | 建议映射起始地址(内核可忽略,通常传 NULL) | NULL 或指定地址 |
length | 映射长度(字节),必须大于 0 | 4096 的整数倍 |
prot | 内存保护标志 | PROT_READ / PROT_WRITE / PROT_EXEC / PROT_NONE |
flags | 映射类型与行为 | MAP_SHARED / MAP_PRIVATE / MAP_ANONYMOUS / MAP_FIXED |
fd | 文件描述符(匿名映射传 -1) | 有效 fd 或 -1 |
offset | 文件偏移量(页对齐) | 0 或页大小整数倍 |
mmap 的两种核心用途
- 文件映射:将磁盘文件的部分内容直接映射到进程地址空间,读写内存即读写文件,无需 read/write 系统调用。
- 匿名映射:不关联文件,分配一块初始化为零的物理内存页,常用于动态内存分配(malloc 大块内存底层即调用 mmap)。
注意:MAP_FIXED 标志会强制内核在指定地址精确创建映射,若该地址已有 VMA 则覆盖之。使用 MAP_FIXED 必须确保地址区间未被占用,否则可能导致进程崩溃。
1.4 mmap 与 VMA 的创建流程
当用户态调用 mmap 时,内核经过以下步骤完成 VMA 的创建与插入:
- 参数校验:检查 length、prot、flags 合法性,对齐 offset 到页边界。
- 地址选择:若 addr 为 NULL,内核在进程地址空间中查找空闲区间;若指定 MAP_FIXED,则直接使用 addr。
- VMA 分配:调用
kmem_cache_alloc分配struct vm_area_struct实例。 - VMA 初始化:填充 vm_start、vm_end、vm_flags、vm_file、vm_ops 等字段。
- 插入红黑树与链表:将新 VMA 插入进程的 VMA 红黑树(快速查找)和双向链表(遍历)。
- 页表建立:若 flags 包含 MAP_POPULATE,立即分配物理页并建立页表映射;否则仅建立 VMA 结构,物理页在缺页时按需分配。
1.5 本章小结
- VMA 是进程地址空间的基本管理单元,每个 VMA 描述一段连续的、具有相同属性的虚拟内存。
- 64 位 Linux 进程地址空间按“代码段 → 数据段 → BSS → 堆 → mmap 区域 → 栈”的经典布局排列。
mmap系统调用是创建 VMA 的主要入口,支持文件映射和匿名映射两种模式。- 内核在 mmap 路径中完成参数校验、地址查找、VMA 分配初始化、数据结构插入等关键步骤。