Linux内核分析之内存管理-06

韩乔落

第6章:虚拟内存区域 (VMA)

基于 Linux 6.12.38 源码


6.1 VMA 概述

6.1.1 什么是 VMA

VMA (Virtual Memory Area,虚拟内存区域) 表示进程地址空间中的一个连续区域,每个 VMA 具有相同的访问权限和属性。

VMA 特点:

  • 连续的虚拟地址范围
  • 相同的访问权限 (读/写/执行)
  • 相同的映射类型 (文件映射、匿名映射)
  • 每个进程的地址空间由多个 VMA 组成

6.1.2 VMA 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
进程地址空间:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 0x400000 ────┐ 代码段 (VMA1) │
│ │ - 可读、可执行 │
│ 0x401000 ────┘ │
│ │
│ 0x600000 ────┐ 数据段 (VMA2) │
│ │ - 可读、可写 │
│ 0x601000 ────┘ │
│ │
│ 0x7fff0000 ─┐ 栈段 (VMA3) │
│ │ - 可读、可写、向下增长 │
│ 0x7ffffffff┘ │
│ │
│ mmap 区域 (多个 VMA): │
│ - 动态库映射 │
│ - 共享内存 │
│ - 匿名映射 │
│ │
└─────────────────────────────────────────────────────────────┘

6.2 vm_area_struct 结构

6.2.1 结构定义

位置: include/linux/mm_types.h:697

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
struct vm_area_struct {
/* 第一个缓存行包含 VMA 树遍历所需的信息 */

union {
struct {
/* VMA 覆盖 [vm_start; vm_end) 地址范围 */
unsigned long vm_start;
unsigned long vm_end;
};
#ifdef CONFIG_PER_VMA_LOCK
struct rcu_head vm_rcu; /* 用于延迟释放 */
#endif
};

/*
* 我们所属的地址空间。
* RCU 读者可以读取此字段。
*/
struct mm_struct *vm_mm;
pgprot_t vm_page_prot; /* 此 VMA 的访问权限 */

/*
* 标志,见 mm.h。
* 要修改请使用 vm_flags_{init|reset|set|clear|mod} 函数。
*/
union {
const vm_flags_t vm_flags;
vm_flags_t __private __vm_flags;
};

#ifdef CONFIG_PER_VMA_LOCK
/*
* 指示从 mm->mm_mt 树分离的区域。
* RCU 读者可以读取此字段。
*/
bool detached;

/*
* 可以在持有时写入 (使用 WRITE_ONCE()):
* - mmap_lock (写模式)
* - vm_lock->lock (写模式)
*/
int vm_lock_seq;

/* RCU 读者可以读取此字段 */
struct vma_lock *vm_lock;
#endif

/*
* 对于有地址空间和后备存储的区域,
* 链接到 address_space->i_mmap 区间树。
*/
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;

/*
* 文件的 MAP_PRIVATE vma 可以同时在 i_mmap 树和 anon_vma 列表中,
* 在 COW 文件页面后。MAP_SHARED vma 只能在 i_mmap 树中。
* 匿名 MAP_PRIVATE、栈或 brk vma (NULL 文件) 只能在 anon_vma 列表中。
*/
struct list_head anon_vma_chain; /* 由 mmap_lock & page_table_lock 序列化 */
struct anon_vma *anon_vma; /* 由 page_table_lock 序列化 */

/* 处理此结构的函数指针 */
const struct vm_operations_struct *vm_ops;

/* 关于我们后备存储的信息 */
unsigned long vm_pgoff; /* 偏移 (在 vm_file 中),以 PAGE_SIZE 为单位 */
struct file *vm_file; /* 我们映射到的文件 (可以为 NULL) */
void *vm_private_data; /* 是 vm_pte (共享内存) */

#ifdef CONFIG_ANON_VMA_NAME
/*
* 对于私有和共享匿名映射,指向以 null 结尾的字符串的指针,
* 包含给 vma 的名称,或如果未命名则为 NULL。
*/
struct anon_vma_name *anon_name;
#endif
#ifdef CONFIG_SWAP
atomic_long_t swap_readahead_info;
#endif
#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU 映射区域 */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* VMA 的 NUMA 策略 */
#endif
#ifdef CONFIG_NUMA_BALANCING
struct vma_numab_state *numab_state; /* NUMA 平衡状态 */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

6.2.2 VMA 核心字段

字段 描述
vm_start VMA 起始虚拟地址
vm_end VMA 结束虚拟地址 (不包含)
vm_mm 所属的 mm_struct
vm_page_prot 页保护权限
vm_flags VMA 标志
vm_ops VMA 操作函数
vm_file 映射的文件 (可为 NULL)
vm_pgoff 文件偏移 (页为单位)

6.3 VMA 标志

6.3.1 VMA 标志定义

位置: include/linux/mm.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* VMA 标志 */
#define VM_READ 0x00000001 /* 可读 */
#define VM_WRITE 0x00000002 /* 可写 */
#define VM_EXEC 0x00000004 /* 可执行 */
#define VM_SHARED 0x00000008 /* 共享映射 */

#define VM_MAYREAD 0x00000010 /* 可能可读 */
#define VM_MAYWRITE 0x00000020 /* 可能可写 */
#define VM_MAYEXEC 0x00000040 /* 可能可执行 */
#define VM_MAYSHARE 0x00000080 /* 可能共享 */

#define VM_GROWSDOWN 0x00000100 /* 向下增长 (栈) */
#define VM_GROWSUP 0x00000200 /* 向上增长 */
#define VM_PFNMAP 0x00000400 /* PFN 映射 */
#define VM_DENYWRITE 0x00000800 /* 拒绝写入 */
#define VM_IO 0x00004000 /* IO 映射 */
#define VM_LOCKED 0x00008000 /* 锁定内存 */
#define VM_SEQ_READ 0x00010000 /* 顺序读 */
#define VM_RAND_READ 0x00020000 /* 随机读 */
#define VM_DONTCOPY 0x00040000 /* fork 时不复制 */
#define VM_DONTEXPAND 0x00080000 /* 不扩展 */
#define VM_LOCKONFAULT 0x00100000 /* 缺页时锁定 */
#define VM_ACCOUNT 0x00200000 /* 计入内存 */
#define VM_NORESERVE 0x00400000 /* 不保留交换空间 */
#define VM_HUGETLB 0x00400000 /* 巨页 */
#define VM_SYNC 0x00800000 /* 同步写 */
#define VM_ARCH_1 0x01000000 /* 架构特定 */
#define VM_WIPEONFORK 0x02000000 /* fork 时清零 */
#define VM_DONTDUMP 0x04000000 /* 不 dump */
#define VM_MIXEDMAP 0x10000000 /* 混合映射 */
#define VM_HUGESPACE 0x40000000 /* 巨页空间 */
#define VM_SOFTDIRTY 0x80000000 /* 软脏位 */

6.3.2 标志操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 初始化 VMA 标志 */
static inline void vm_flags_init(struct vm_area_struct *vma,
vm_flags_t flags);

/* 重置 VMA 标志 */
static inline void vm_flags_reset(struct vm_area_struct *vma,
vm_flags_t flags);

/* 设置 VMA 标志 */
static inline void vm_flags_set(struct vm_area_struct *vma,
vm_flags_t flags);

/* 清除 VMA 标志 */
static inline void vm_flags_clear(struct vm_area_struct *vma,
vm_flags_t flags);

/* 修改 VMA 标志 */
static inline void vm_flags_mod(struct vm_area_struct *vma,
vm_flags_t set, vm_flags_t clear);

6.4 VMA 操作

6.4.1 vm_operations_struct

位置: include/linux/mm.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct vm_operations_struct {
/* 打开 VMA */
void (*open)(struct vm_area_struct * area);

/* 关闭 VMA */
void (*close)(struct vm_area_struct * area);

/* 缺页处理 (读) */
vm_fault_t (*fault)(struct vm_fault *vmf);

/* 缺页处理 (写,COW) */
vm_fault_t (*page_mkwrite)(struct vm_fault *vmf);

/* 页面错误 (写保护) */
vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf);

/* 访问许可 */
vm_fault_t (*access)(struct vm_fault *vmf);

/* 分裂 huge page */
vm_fault_t (*split)(struct vm_fault *vmf);

/* 页面标记脏 */
vm_fault_t (*pmd_fault)(struct vm_fault *vmf, unsigned long address);

/* 页面迁移 */
unsigned long (*pagesize)(struct vm_area_struct * area);

/* mprotect 通知 */
int (*mprotect)(struct vm_area_struct *vma,
unsigned long start, unsigned long end,
unsigned long newflags);

/* 匿名 VMA 名称 */
void (*vma_name)(struct vm_area_struct *vma, char *name, size_t count);
};

6.4.2 VMA 查找操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 查找包含地址的 VMA */
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr);

/* 查找并锁定 VMA */
struct vm_area_struct *lock_vma_under_rcu(struct mm_area_struct *mm,
unsigned long addr);

/* 查找最近的 VMA */
struct vm_area_struct *find_vma_prev(struct mm_struct *mm,
unsigned long addr,
struct vm_area_struct **pprev);

/* 查找 VMA 交集 */
struct vm_area_struct *find_vma_intersection(struct mm_struct *mm,
unsigned long start_addr,
unsigned long end_addr);

6.4.3 VMA 创建/删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 插入 VMA */
int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma);

/* 创建 VMA */
struct vm_area_struct *vm_area_alloc(struct mm_struct *mm);

/* 释放 VMA */
void vm_area_free(struct vm_area_struct *vma);

/* 合并 VMA */
struct vm_area_struct *vma_merge(struct mm_struct *mm,
struct vm_area_struct *prev,
unsigned long addr, unsigned long end,
unsigned long vm_flags,
pgprot_t vm_prot,
struct mempolicy *policy,
unsigned long vm_pgoff,
struct anon_vma *anon_vma,
struct file *file, pgoff_t vm_pgoff);

6.5 VMA 管理

6.5.1 Maple Tree 管理

Linux 5.13+ 使用 Maple Tree 管理 VMA,替代了之前的红黑树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Maple Tree 操作 */
void __vma_link_list(struct vm_area_struct *vma,
struct vm_area_struct *prev,
struct vm_area_struct *next);

void __vma_unlink_list(struct mm_struct *mm,
struct vm_area_struct *vma);

/* Maple Tree 查找 */
struct vm_area_struct *vma_lookup(struct mm_struct *mm,
unsigned long addr);

/* 获取第一个 VMA */
struct vm_area_struct *vma_first(struct mm_struct *mm);

/* 获取下一个 VMA */
struct vm_area_struct *vma_next(struct vm_area_struct *vma);

/* 获取上一个 VMA */
struct vm_area_struct *vma_prev(struct vm_area_struct *vma);

6.5.2 VMA 分裂和合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 分裂 VMA */
int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, int new_below);

/* 合并 VMA */
struct vm_area_struct *vma_merge(struct mm_struct *mm,
struct vm_area_struct *prev,
unsigned long addr, unsigned long end,
unsigned long vm_flags, pgprot_t vm_prot,
struct mempolicy *policy,
unsigned long vm_pgoff,
struct anon_vma *anon_vma,
struct file *file);

/* 检查是否可以合并 */
int vma_can_merge(struct vm_area_struct *vma, struct vm_area_struct *next);

6.5.3 VMA 调整大小

1
2
3
4
5
6
7
8
9
10
11
/* 扩展栈 */
int expand_stack(struct vm_area_struct *vma, unsigned long address);

/* 扩展 VMA */
struct vm_area_struct *expand_vma(struct vm_area_struct *vma,
unsigned long delta);

/* 调整 VMA 大小 */
int vma_adjust(struct vm_area_struct *vma, unsigned long start,
unsigned long end, pgoff_t pgoff,
struct vm_area_struct *insert);

6.6 匿名 VMA

6.6.1 anon_vma 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
struct anon_vma {
struct anon_vma *root; /* 根 anon_vma */
struct rw_semaphore rwsem; /* 读写锁 */
atomic_long_t refcount; /* 引用计数 */

/*
* 底层红黑树的根节点,按地址排序
*/
struct rb_root_cached rb_root;

/* 此 anon_vma 所属的 VMA 列表 */
struct list_head head;
};

6.6.2 anon_vma_chain

1
2
3
4
5
6
7
struct anon_vma_chain {
struct vm_area_struct *vma;
struct anon_vma *anon_vma;
struct list_head same_vma; /* 同 VMA 的链表 */
struct rb_node rb; /* 按 addr 排序 */
unsigned long rb_subtree_last;
};

6.6.3 匿名 VMA 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 分配 anon_vma */
struct anon_vma *anon_vma_alloc(void);
void anon_vma_free(struct anon_vma *anon_vma);

/* 关联 anon_vma */
int anon_vma_fork(struct vm_area_struct *vma,
struct vm_area_struct *pvma);

/* 获取 anon_vma */
struct anon_vma *page_get_anon_vma(struct page *page);

/* 匿名 VMA 名称 (用于 /proc/[pid]/maps) */
struct anon_vma_name *anon_vma_name_alloc(const char *name);
void anon_vma_name_free(struct kref *kref);

6.7 VMA 锁

6.7.1 Per-VMA Lock

Linux 6.1+ 引入了 Per-VMA 锁机制,减少 mmap_lock 的竞争。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct vma_lock {
struct rw_semaphore lock;
};

/* VMA 锁操作 */
void vma_start_read(struct vm_area_struct *vma);
void vma_end_read(struct vm_area_struct *vma);
void vma_start_write(struct vm_area_struct *vma);
void vma_end_write(struct vm_area_struct *vma);

/* 尝试锁定 */
bool vma_start_read_trylock(struct vm_area_struct *vma);
bool vma_start_write_trylock(struct vm_area_struct *vma);

6.7.2 锁定顺序

1
2
3
4
5
6
7
8
VMA 锁的锁定顺序:
1. mmap_lock (读或写)
2. vm_lock->lock (读或写)

VMA 锁的使用场景:
- 缺页处理
- 页面错误处理
- VMA 查找

6.8 本章小结

本章介绍了 Linux 6.12 的虚拟内存区域 (VMA):

  1. VMA 概述: 进程地址空间中的连续区域,相同权限和属性
  2. vm_area_struct: VMA 核心数据结构
  3. VMA 标志: 访问权限、增长方向、映射类型等
  4. VMA 操作: 查找、创建、删除、合并、分裂
  5. Maple Tree: Linux 5.13+ 使用的 VMA 管理数据结构
  6. 匿名 VMA: 匿名映射的 VMA 管理
  7. VMA 锁: Per-VMA 锁机制,减少竞争

下一章将介绍页面分配。

  • Title: Linux内核分析之内存管理-06
  • Author: 韩乔落
  • Created at : 2026-01-20 21:39:13
  • Updated at : 2026-02-24 14:05:18
  • Link: https://jelasin.github.io/2026/01/20/Linux内核分析之内存管理-06/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments