内存管理-77-kernel-internals系列-Memory专题-Advanced-7-Maple Tree
一、Maple Tree: VMA Management Data Structure 翻译
注: 翻译自 https://kernel-internals.org/mm/maple-tree/
Maple Tree:VMA 管理数据结构
取代了 Linux 6.1 中用于虚拟内存区域(VMA)管理的 rbtree + 链表 的 B-tree 变体。
1. 关键源文件
------------------------------------------------------------------------------------------------------------------------------------- 文件 用途 ------------------------------------------------------------------------------------------------------------------------------------- include/linux/maple_tree.h 公共 API:struct maple_tree、struct ma_state、所有宏和内联函数 lib/maple_tree.c 完整实现:maple_tree_init()、mas_walk()、mas_find()、mas_store()、mas_erase() include/linux/mm_types.h struct mm_struct 的嵌入(mm_mt)、struct vma_iterator、MM_MT_FLAGS mm/mmap.c find_vma()、find_vma_intersection()、使用 __mt_destroy() 的进程地址空间销毁 mm/vma.c 通过 vma_iter_store_gfp() 插入 VMA,通过 vma_iter_clear() 删除 VMA kernel/fork.c mm_init():使用 mt_init_flags() 初始化 maple tree -------------------------------------------------------------------------------------------------------------------------------------
2. 为什么 Maple Tree 取代了 rbtree + 链表
在 Linux 6.1 之前,每个 mm_struct 都为 VMA 维护了两套冗余的数据结构:
(1) 一棵以 vm_start 为键的红黑树(mm->mm_rb),提供按地址的 O(log n) 查找。
(2) 一条将所有 VMA 按地址顺序串起来的双向链表(vma->vm_next / vma->vm_prev),用于顺序遍历。
每次 VMA 的插入、删除或修改,都必须在 mmap_lock 保护下原子地更新这两个结构。除了代码复杂度之外,还有两个具体的性能代价:
(1) 顺序扫描时的缓存缺失。 遍历链表需要逐个跟随 vm_next 指针,每个指针可能指向不同 slab 对象中的不同缓存行。对于有数千个映射的进程,这会变成一长串追逐指针的访存。
(2) 内存开销。 每个 vm_area_struct 仅为索引就携带了三个 rbtree 节点字段(rb_node,以及嵌入在指针中的父指针和颜色)和两个链表指针,每个 VMA 约 40 字节。
Maple tree(在 v6.1 合入,由 Oracle 的 Liam Howlett 和 Matthew Wilcox 编写)将这两种结构统一为一种 B-tree 变体,原生支持按范围查找和高效的顺序遍历。因为 B-tree 节点将多个 pivot 和 slot 打包在一个 256 字节的缓存行中,一次节点访问就可以完成多次 pivot 比较,与 rbtree 中每次比较都要加载不同节点相比,大大减少了缓存缺失。
3. 什么是 Maple Tree
Maple tree 是一种支持 RCU 安全、按键范围索引的 B-tree。它存储互不重叠的 unsigned long 键范围,将每个范围映射到一个不透明指针。对于 VMA 管理,键是地址范围 [vm_start, vm_end - 1],值是 struct vm_area_struct * 。
3.1 节点类型和大小
定义了三种节点类型(来自 include/linux/maple_tree.h):
/* 64位上:每个节点256字节,按256字节对齐 */ #define MAPLE_NODE_SLOTS 31 /* 通用节点的 slot 数 */ #define MAPLE_RANGE64_SLOTS 16 /* range节点:15个pivot + 16个slot */ #define MAPLE_ARANGE64_SLOTS 10 /* alloc-range节点:9个pivot + 10个slot + gap */
实际使用的两种节点变体:
(1) maple_range_64 —— 标准的内部/叶子节点
struct maple_range_64 { struct maple_pnode *parent; unsigned long pivot[MAPLE_RANGE64_SLOTS - 1]; /* 15个范围边界 */ union { void __rcu *slot[MAPLE_RANGE64_SLOTS]; /* 16个孩子或值指针 */ struct { void __rcu *pad[MAPLE_RANGE64_SLOTS - 1]; struct maple_metadata meta; /* 结束偏移 + 最大空洞 */ }; }; };
一个 maple_range_64 节点在 256 字节内存储最多 15 个 pivot 和 16 个 slot。用 B-tree 的术语说,pivot 是每个 slot 中存储范围的包含性上界。Slot i 覆盖范围 (pivot[i-1], pivot[i]],slot 0 从该节点的隐含最小值开始覆盖。
(2) maple_arange_64 —— 分配范围节点
struct maple_arange_64 {
struct maple_pnode *parent;
unsigned long pivot[MAPLE_ARANGE64_SLOTS - 1]; /* 9个范围边界 */
void __rcu *slot[MAPLE_ARANGE64_SLOTS]; /* 10个指针 */
unsigned long gap[MAPLE_ARANGE64_SLOTS]; /* 每个子树中最大的NULL空洞 */
struct maple_metadata meta;
};
分配范围节点额外记录每个子树中连续 NULL 表项的最大空洞。这支持 mas_empty_area() / mas_empty_area_rev(),无需扫描整棵树就能找到给定大小的空闲地址范围——这对 mmap() 的地址选择至关重要。
3.2 节点分配
所有节点都从启动时由 maple_tree_init() 创建的名为 maple_node 的专用 kmem_cache 中分配:
void __init maple_tree_init(void)
{
struct kmem_cache_args args = {
.align = sizeof(struct maple_node),
.sheaf_capacity = 32,
};
maple_node_cache = kmem_cache_create("maple_node", sizeof(struct maple_node), &args, SLAB_PANIC);
}
256 字节对齐保证节点指针的低 8 位恒为零。Maple tree 利用这些位直接在指针中编码节点类型和树的元数据,避免额外的内存访问。
3.3 树的最大高度
#define MAPLE_HEIGHT_MAX 31
每个 maple_range_64 节点有 16 个 slot,高度为 31 的树可以表示 16^31 个不同的范围——远远超过任何现实的地址空间。
4. struct maple_tree
/* include/linux/maple_tree.h */
struct maple_tree {
union {
spinlock_t ma_lock;
#ifdef CONFIG_LOCKDEP
struct lockdep_map *ma_external_lock;
#endif
};
unsigned int ma_flags;
void __rcu *ma_root;
};
ma_lock —— 内部自旋锁,在没有提供外部锁时使用。对于 mm->mm_mt 未使用:该树配置了 MT_FLAGS_LOCK_EXTERN,由 mmap_lock 作为串行化锁。
ma_flags —— 树全局属性的位掩码(见下文)。
ma_root —— 受 RCU 保护的根节点指针(如果树在索引 0 处恰好只有一个表项,则为单个存储值)。
4.1 标志
#define MT_FLAGS_ALLOC_RANGE 0x01 /* 跟踪每个节点的最大空闲空洞 */ #define MT_FLAGS_USE_RCU 0x02 /* 节点通过RCU释放 */ #define MT_FLAGS_LOCK_IRQ 0x100 /* ma_lock以IRQ安全方式获取 */ #define MT_FLAGS_LOCK_BH 0x200 /* ma_lock以BH安全方式获取 */ #define MT_FLAGS_LOCK_EXTERN 0x300 /* 外部锁;ma_lock未使用 */
4.2 mm_struct 如何嵌入 maple tree
/* include/linux/mm_types.h */ struct mm_struct { ... struct maple_tree mm_mt; ... struct rw_semaphore mmap_lock; ... }; /* 初始化 mm->mm_mt 时使用的标志 */ #define MM_MT_FLAGS (MT_FLAGS_ALLOC_RANGE | MT_FLAGS_LOCK_EXTERN | MT_FLAGS_USE_RCU)
mm->mm_mt 同时启用了这三个标志:
MT_FLAGS_ALLOC_RANGE —— 启用空洞跟踪,使 mmap() 能高效找到空闲区域。
MT_FLAGS_LOCK_EXTERN —— 绕过树的内部 ma_lock 自旋锁;调用者在任何修改前必须持有 mmap_lock 写锁。
MT_FLAGS_USE_RCU —— 被删除的节点通过 call_rcu() 而不是立即释放,从而支持无锁的 RCU 读者(见下文 [RCU 安全])。
该树在 mm_init()(kernel/fork.c)中初始化并绑定到 mmap_lock:
static struct mm_struct *mm_init(struct mm_struct *mm, ...) {
mt_init_flags(&mm->mm_mt, MM_MT_FLAGS);
mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock);
...
}
5. RCU 安全
Maple tree 通过确保从树中移除的节点,在所有当前正在执行的 RCU 读端临界区完成后才被释放,从而实现无锁读。当设置了 MT_FLAGS_USE_RCU 时,删除路径会调用 call_rcu() 而不是立即释放:
/* lib/maple_tree.c —— 节点删除路径 */
if (mt_in_rcu(mt)) {
mt_destroy_walk(enode, mt, false);
call_rcu(&node->rcu, mt_free_walk); /* 延迟释放 */
} else {
mt_destroy_walk(enode, mt, true); /* 立即释放 */
}
struct maple_node 在节点从树中摘除后,将其 slot[] 数组复用为 rcu_head(两者存在于 union 中)。在节点被摘除前从 slots[] 加载了指针的读者可以安全地继续解引用它;该内存在其 RCU 宽限期结束前不会归还给分配器。
5.1 mm 中 RCU 模式的生命周期
mm->mm_mt 大多数时间以启用 RCU 的模式运行。在进程销毁和 dup_mmap() 路径中,内核会临时禁用 RCU 模式,以避免在批量丢弃节点时延迟释放的开销:
/* mm/mmap.c —— exit_mmap() */ mt_clear_in_rcu(&mm->mm_mt); /* 销毁时切换为立即释放 */ ... __mt_destroy(&mm->mm_mt); /* 释放所有剩余节点 */ /* mm/mmap.c —— dup_mmap() —— 批量复制完成后 */ mt_set_in_rcu(vmi.mas.tree); /* 在新的mm上重新启用RCU */
mt_set_in_rcu() 和 mt_clear_in_rcu() 在树的锁保护下开关 MT_FLAGS_USE_RCU。
5.2 无锁 VMA 查找(per-VMA 锁快路径)
仅持有 rcu_read_lock() 而不持有 mmap_lock 的读者,可以安全地遍历 mm->mm_mt 来查找 VMA。这是 per-VMA lock 快速路径的基础:
/* mm/mmap_lock.c —— lock_vma_under_rcu() */ MA_STATE(mas, &mm->mm_mt, address, address); rcu_read_lock(); vma = mas_walk(&mas); /* 在RCU下无锁遍历maple tree */ if (!vma) goto inval; vma = vma_start_read(mm, vma); /* 尝试获取per-VMA读锁 */ ... rcu_read_unlock();
mas_walk() 仅通过读取 __rcu 注解的指针和 pivot,从根走到叶子。不需要加锁,因为被删除的节点在至少一个 RCU 宽限期内仍然有效。
6. MA_STATE 和 Maple State 游标
struct ma_state 是所有高级 maple tree API 的操作游标。它跟踪树中的当前位置,使顺序操作(例如遍历所有 VMA)无需每次都从根重新开始。
/* include/linux/maple_tree.h */
struct ma_state {
struct maple_tree *tree; /* 正在操作的树 */
unsigned long index; /* 当前操作的范围起点 */
unsigned long last; /* 当前操作的范围终点 */
struct maple_enode *node; /* 当前节点 */
unsigned long min; /* 当前节点的隐含最小值 */
unsigned long max; /* 当前节点的隐含最大值 */
struct slab_sheaf *sheaf; /* 预分配的节点池 */
struct maple_node *alloc; /* 单个预分配节点(快路径) */
unsigned long node_request; /* 预分配的节点数 */
enum maple_status status; /* ma_start / ma_active / ma_none / ma_error / ... */
unsigned char depth; /* 写路径下潜时的深度 */
unsigned char offset; /* 当前节点内的slot/pivot索引 */
unsigned char mas_flags;
unsigned char end; /* 当前节点中最后使用的slot */
enum store_type store_type; /* 需要的写类型 */
};
status 字段决定下一次操作如何对待游标:注: 6.1.115内核上还没有这个 status 字段。
------------------------------------------------------------------------------------------------------------------------------------- 状态 含义 ------------------------------------------------------------------------------------------------------------------------------------- ma_start 尚未定位;下一次操作从根开始遍历 ma_active 游标在 node / offset 处有效;可安全原地继续 ma_none 搜索完成;在 index 处未找到表项 ma_root 在根指针中找到表项(树只有一个表项) ma_pause 节点数据可能已过期;需要重启(例如锁曾被释放) ma_overflow 到达搜索上限 ma_underflow 到达搜索下限 ma_error 发生错误;node 编码了负的 errno -------------------------------------------------------------------------------------------------------------------------------------
MA_STATE 宏在栈上初始化游标:
#define MA_STATE(name, mt, first, end) \ struct ma_state name = { \ .tree = mt, \ .index = first, \ .last = end, \ .node = NULL, \ .status = ma_start, \ .min = 0, \ .max = ULONG_MAX, \ ... \ }
操作完成后,mas->index 和 mas->last 保存找到或存储的表项的完整范围。在 mm/mmap.c 和 mm/vma.c 中广泛使用的 struct vma_iterator 是一个薄封装:
/* include/linux/mm_types.h */
struct vma_iterator {
struct ma_state mas;
};
#define VMA_ITERATOR(name, __mm, __addr) \
struct vma_iterator name = { \
.mas = { \
.tree = &(__mm)->mm_mt, \
.index = __addr, \
.status = ma_start, \
}, \
}
7. 关键操作
7.1 树生命周期:mt_init() 和 mtree_destroy()
/* 以默认标志初始化空树(无外部锁,无RCU) */
static inline void mt_init(struct maple_tree *mt) {
mt_init_flags(mt, 0);
}
/* 以指定标志初始化(用于 mm->mm_mt) */
static inline void mt_init_flags(struct maple_tree *mt, unsigned int flags);
/* 销毁树并释放所有节点(内部获取 ma_lock) */
void mtree_destroy(struct maple_tree *mt);
/* 无锁销毁——调用者必须持有外部锁 */
void __mt_destroy(struct maple_tree *mt);
对于 mm->mm_mt,mm_init() 使用 MM_MT_FLAGS 调用 mt_init_flags()。在 exit_mmap() 和 dup_mmap() 错误路径中的销毁,使用 __mt_destroy(&mm->mm_mt),此时调用者已持有 mmap_lock 写锁。
7.2 mas_walk() —— 将游标定位到 index
void *mas_walk(struct ma_state *mas);
在树中搜索 mas->index。返回时,mas->index 和 mas->last 被设置为找到的表项的范围(如果没找到,则为 0 和 ULONG_MAX)。不会推进游标;在同一 index 上调用两次 mas_walk() 返回同一表项。
可以仅在 rcu_read_lock() 下安全调用(无需写锁)。per-VMA 锁快路径直接使用它:
MA_STATE(mas, &mm->mm_mt, address, address);
rcu_read_lock();
vma = mas_walk(&mas);
7.3 mas_find() —— 从 index 向前迭代
void *mas_find(struct ma_state *mas, unsigned long max);
返回范围与 [mas->index, max] 重叠的下一个表项。成功返回后,mas->index 和 mas->last 描述返回表项的范围,游标定位于此;下一次调用从 mas->last + 1 开始向前扫描。当剩余范围内没有表项时返回 NULL(状态设为 ma_overflow)。必须在持有 rcu_read_lock() 或写锁时调用。
mas_for_each() 宏使用它:
#define mas_for_each(__mas, __entry, __max) \
while (((__entry) = mas_find((__mas), (__max))) != NULL)
7.4 mas_store() / mas_store_gfp() —— 插入或更新范围映射
/* 在 [mas->index, mas->last] 存储entry。需要预分配节点。 */ void *mas_store(struct ma_state *mas, void *entry); /* 按需分配的存储(失败返回 -ENOMEM)。 */ int mas_store_gfp(struct ma_state *mas, void *entry, gfp_t gfp);
两个函数都将 entry 写入 [mas->index, mas->last] 范围,调用前必须设置好该范围。如果已有表项与该范围重叠,会被挤出(按需分割、收缩或替换)。mas_store() 返回第一个被挤出的表项(如果有);mas_store_gfp() 返回 0 或 -ENOMEM。
需要写锁。
在 VMA 代码中,vma_iter_store_gfp() 封装了 mas_store_gfp():
/* mm/vma.h */ static inline int vma_iter_store_gfp(struct vma_iterator *vmi, struct vm_area_struct *vma, gfp_t gfp) { __mas_set_range(&vmi->mas, vma->vm_start, vma->vm_end - 1); mas_store_gfp(&vmi->mas, vma, gfp); if (unlikely(mas_is_err(&vmi->mas))) return -ENOMEM; vma_mark_attached(vma); return 0; }
7.5 mas_erase() —— 删除当前位置的表项
void *mas_erase(struct ma_state *mas);
找到包含 mas->index 的范围,擦除整个范围(存入 NULL),并返回之前的表项。返回时,mas->index 和 mas->last 被设置为被擦除的范围。
需要写锁。
在 VMA 代码中,删除路径使用 vma_iter_clear(),它以 NULL 调用 mas_store_prealloc():
/* mm/vma.h */
static inline void vma_iter_clear(struct vma_iterator *vmi) {
mas_store_prealloc(&vmi->mas, NULL);
}
7.6 mt_for_each() —— 简单的全树遍历
/* include/linux/maple_tree.h */
#define mt_for_each(__tree, __entry, __index, __max) \
for (__entry = mt_find(__tree, &(__index), __max); \
__entry; \
__entry = mt_find_after(__tree, &(__index), __max))
mt_for_each 使用更简单的 mt_find() / mt_find_after() 接口(每次调用内部获取并释放 rcu_read_lock()),而不是显式的 ma_state。适合不需要在迭代之间维护游标状态的只读遍历。
8. 代码示例
8.1 按地址查找 VMA
/* find_vma() —— mm/mmap.c */
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
unsigned long index = addr;
mmap_assert_locked(mm);
return mt_find(&mm->mm_mt, &index, ULONG_MAX);
}
mt_find() 从 index 开始,返回覆盖 addr 或紧随其后的第一个表项。对于地址查找,这等价于问“哪个 VMA 覆盖 addr,如果没有,下一个是哪个”。
8.2 mmap 期间插入 VMA
/* mm/vma.c —— 匿名映射创建(简化) */
vma = vm_area_alloc(mm);
vma_set_range(vma, addr, addr + len, pgoff);
vm_flags_init(vma, vm_flags);
vma_start_write(vma); /* 标记VMA为写锁定 */
if (vma_iter_store_gfp(vmi, vma, GFP_KERNEL))
goto mas_store_fail;
mm->map_count++;
8.3 遍历所有 VMA(例如用于 /proc/PID/maps)
VMA_ITERATOR(vmi, mm, 0);
struct vm_area_struct *vma;
for_each_vma(vmi, vma) {
/* vma->vm_start, vma->vm_end, vma->vm_flags ... */
}
for_each_vma 基于 vma_next(),它让 vma_iterator 在 maple tree 中向前推进,而无需从根重新开始。
8.4 查找空闲地址范围(mmap 提示搜索)
/* mas_empty_area() —— 在 [min, max] 中找至少 size 字节的空洞 */ MA_STATE(mas, &mm->mm_mt, 0, 0); mas_empty_area(&mas, mmap_min_addr, mmap_end - len, len); addr = mas.index;
这利用 maple_arange_64 节点中的 gap[] 数组跳过不可能包含足够大空洞的子树,使空闲范围搜索为 O(log n) 而不是 O(n)。
9. 与 rbtree 的性能对比
------------------------------------------------------------------------------------------------------------------------------------- 属性 rbtree + 链表 Maple tree ------------------------------------------------------------------------------------------------------------------------------------- 点查找 O(log n) O(log n) 顺序扫描 O(n) 指针追逐 O(n) 顺序节点读取 空闲空洞搜索 O(n) 全扫描 O(log n),通过 gap 元数据 每节点缓存行 1(每个节点一个 VMA 指针) 每个 256 字节节点最多 16 个表项 每个 VMA 的内存(索引开销) ~40 字节(rb_node + 链表指针) 摊销 ~16 字节(共享节点) 读锁 需要 mmap_lock RCU 读锁即可 更新复杂度 更新两个结构 更新一棵树 -------------------------------------------------------------------------------------------------------------------------------------
对于顺序扫描(例如读取 /proc/PID/maps 或对大范围 mprotect()),关键优势在于一个 maple_range_64 节点在一个 256 字节分配中容纳 16 个 VMA 指针。遍历一个满节点只需一次缓存缺失加载节点,然后得到最多 16 个有效表项,而跟随链表时每个表项需要一次cache-miss。对于有数千个映射的地址空间(JVM、大型应用、容器运行时),这种缓存压力的降低是可测量的。
10. 调试
10.1 /proc/PID/maps 和 /proc/PID/smaps
这些文件通过直接遍历 mm->mm_mt 生成。读取它们无需任何改动;maple tree 是权威来源。
10.2 CONFIG_DEBUG_MAPLE_TREE
通过 lib/Kconfig.debug 启用:
CONFIG_DEBUG_MAPLE_TREE=y # 需要 CONFIG_DEBUG_KERNEL=y
启用后:
(1) MT_BUG_ON()、MAS_BUG_ON() 和 MT_WARN_ON() 在每次树操作时执行额外的一致性检查,失败时转储整棵树。
(2) 在每次写入前,mas_store() 内部执行额外的范围校验。
(3) 可以显式调用 mt_validate() 检查任何树的结构完整性:
void mt_validate(struct maple_tree *mt);
mt_validate() 检查所有 pivot 数组是否有序、没有范围重叠、gap 元数据一致,以及 ma_flags 中存储的高度与实际树深是否匹配。
10.3 mt_dump()
void mt_dump(const struct maple_tree *mt, enum mt_dump_format format);
以十进制(mt_dump_dec)或十六进制(mt_dump_hex)格式将整棵树打印到内核日志。仅在 CONFIG_DEBUG_MAPLE_TREE=y 时可用。MT_BUG_ON() 和 MAS_BUG_ON() 失败时会自动调用。
10.4 mas_dump()
void mas_dump(const struct ma_state *mas);
打印 ma_state 游标的当前状态(index、last、节点指针、status、min/max、depth、offset)。
用于调试不正确的游标状态时很有用。
10.4 跟踪 maple tree 操作
Maple tree 有内置 tracepoint(trace_ma_read、trace_ma_write、trace_ma_op),分布在 lib/maple_tree.c 中。当相应的 maple_tree 事件组启用时,可通过 ftrace 查看,无需 CONFIG_DEBUG_MAPLE_TREE 即可对每次树操作提供可见性。
10.5 RCU 模式与调试
当 CONFIG_DEBUG_MAPLE_TREE 与 CONFIG_MAPLE_RCU_DISABLED(lib/maple_tree.c 顶部的编译时选项)结合时,RCU 释放被完全抑制,节点被立即释放。这有助于暴露树使用者的 use-after-free bug,但不适合生产环境。
11. 与其他子系统的关系
(1) Per-VMA 锁(mm/mmap_lock.c)依赖 MT_FLAGS_USE_RCU 在 rcu_read_lock() 下执行无锁 VMA 查找。
(2) /proc/PID/maps 在持有 mmap_lock 读锁时,通过 for_each_vma() 遍历 mm->mm_mt。
(3) dup_mmap() 使用 __mt_dup() 将父进程 maple tree 批量复制到子进程 mm,然后用 vma_iter_clear_gfp() 替换 VM_DONTCOPY 表项。
(4) execve() 通过调用 exit_mmap() 销毁地址空间,exit_mmap() 在清除所有页表项后调用 __mt_destroy(&mm->mm_mt)。
(5) vmalloc 使用红黑树(free_vmap_area_root,struct rb_root)跟踪内核虚拟地址空间——不是 maple tree。
12. 延伸阅读
LWN:The maple tree, a modern data structure for a complex problem —— 由 Liam Howlett 和 Matthew Wilcox 于 2022 年撰写的文章,解释了设计原理、节点布局,以及与 rbtree + 链表组合对比的基准测试结果。
lib/maple_tree.c —— 完整实现:maple_tree_init()、mas_walk()、mas_find()、mas_store()、mas_erase()、通过 mt_free_walk() 实现的 RCU 节点释放,以及 CONFIG_MAPLE_RCU_DISABLED 调试模式。
include/linux/maple_tree.h —— 公共 API:struct maple_tree、struct ma_state、MA_STATE()、mt_for_each()、节点类型常量以及所有标志定义。
提交 54a611b60590 —— 在 Linux 6.1 中合入 maple tree、取代 mm->mm_rb 和 VMA 链表的提交;合并信息中链接了完整的补丁系列和基准测试结果。
per-vma-locks.md —— per-VMA 锁快路径,依赖 MT_FLAGS_USE_RCU,在不持有 mmap_lock 的情况下,在 rcu_read_lock() 下执行无锁的 maple tree 遍历。
LWN:Splitting VMAs —— 介绍了 Linux 6.7 中的 VMA 树重组工作,该工作得益于更简洁的 maple tree API,将 VMA 操作辅助函数移入了 mm/vma.c。
posted on 2026-10-08 10:45 Hello-World3 阅读(3) 评论(0) 收藏 举报
浙公网安备 33010602011771号