1. anon_vma_chain 在 fork 时父→子的 VMA 链接与子 anon_vma 副本的工程价值?
在 fork 创建子进程时,内核如何通过 anon_vma_chain 建立父进程 VMA 到子进程 anon_vma 的链接,为什么需要为子进程创建 anon_vma 副本,这背后的工程价值是什么?
- fork 时 anon_vma 与 anon_vma_chain 的建立逻辑
- 父子进程共享匿名页的反向映射组织
- 防止重复遍历与 O(N^2) 复杂度
fork 时父进程的每个匿名 VMA 会通过 anon_vma_chain 建立一条"父 VMA → 父 anon_vma"的链接,同时为子进程创建新的 anon_vma 副本,并建立"子 VMA → 子 anon_vma"的链接;这两个 anon_vma 通过 anon_vma 的 parent 指针与红黑树形成父子关系(父 anon_vma 的 rb_root 指向子 anon_vma)。这样当一个匿名页被某个进程的 rmap 遍历时,rmap_walk 会沿着该 anon_vma 的 rb 树遍历到所有后代 anon_vma,从而低成本找到所有映射该页的 VMA。其工程价值在于:如果只用一个共享的 anon_vma,那么在 fork 后再次 fork 时,链会把所有进程的 VMA 串成一条长链,rmap 遍历会退化为 O(N) 且需要反复比对,容易重复遍历;而"每个 VMA 一个 anon_vma + 树形父链"的结构让每个 VMA 只被精确遍历一次,并能在 unmap 单页时快速找到该页对应的全部映射,而无需扫描整棵进程地址空间。
匿名页反向映射的核心难点是"一个物理页可能被多个进程/多个 VMA 映射",而 anon_vma_chain 把"VMA → anon_vma"的对应关系显式记录下来,rmap walk 只需沿 anon_vma 的树形结构走,而不用遍历整个进程的 mm。这正是 rmap 在 fork 场景下保持可扩展性的关键。