内存管理-66-RMAP

基于 msm-5.4

一、RMAP简介

 


二、相关结构

1. struct anon_vma

struct anon_vma { //rmap.h
    struct anon_vma *root;
    struct rw_semaphore rwsem;
    atomic_t refcount;
    unsigned degree;
    struct anon_vma *parent;
    struct rb_root_cached rb_root;
};

struct anon_vma 是“匿名页家族”的锚点,核心用途是给匿名页反查 VMA。解决的是“页怎么找到一组相关 VMA。

 

1.1 成员介绍:

root:

含义:指向这棵 anon_vma 树的根。fork 之后,父子进程的匿名页关系会形成一棵“血缘树”。一个页上记录的 page->mapping 对匿名页来说,最终会落到某个 anon_vma 上。做 rmap 遍历时,很多时候只要锁整棵树的公共根即可。

直观理解:root 不是“我的父节点”,而是“我所属家族的族长”。同一棵 anon_vma 家族树里的所有节点,root 都相同。

总结来说就是:当前 anon_vma 所在家族树的根。同一血缘树上的 anon_vma 共享同一个 root。锁通常围绕 root 来统一管理。


rwsem:

含义:保护这棵 anon_vma 关系树及其区间树的读写锁。增删 anon_vma_chain、插删 rb_root 节点时持写锁。rmap walk 时遍历 anon_vma->rb_root 持读锁。

实际上锁的是 anon_vma->root->rwsem 这一把大锁。所以你会看到很多代码不是锁“当前 anon_vma”,而是通过 root 去锁整棵树。


refcount:

含义:引用计数,保证 anon_vma 在并发路径里不会被提前释放。页反查、回收、迁移、page_referenced() 这类路径,可能先从页拿到一个 anon_vma 指针,再去走 rmap。这时必须确保该 anon_vma 在整个操作期间有效。
典型场景:
folio_get_anon_vma() 这种路径会先尝试拿引用。
put_anon_vma() 则在最后归还引用,可能触发对象释放。


degree:

degree 表示"以我为 parent 的后代数 + 以我为主 anon_vma 的 VMA 数"。

连接度计数(5.4内核里把“孩子数 + 活跃VMA连接数”合并表达为一个字段)。典型增加来源:有 VMA 的 vma->anon_vma 指向它;有子 anon_vma 的 parent 指向它。典型减少来源:子关系拆除;VMA 主连接拆除。
当 degree 归零且区间树为空时,anon_vma 才进入可释放状态。


parent:

指向父 anon_vma,用于组织 fork/COW 产生的血缘关系。####


rb_root:

该 anon_vma 下的区间树根,树节点是 anon_vma_chain。用于按 pgoff 范围快速反查“哪些 VMA 可能映射了该匿名页”。


1.2 补充介绍:

(1) 为什么 anon_vma->rb_root 里存的是 AVC,不是 VMA: 因为一个 VMA 可能同时属于多个 anon_vma, 树里存“关系节点”比直接存 VMA 更自然,也能兼容双向组织。

(2) root 和 parent 的区别:parent 是直接父节点,root 是整棵家族树的公共根。锁、rmap 家族归属更多靠 root,血缘分叉关系更多靠 parent。


2. struct anon_vma_chain

struct anon_vma_chain { //rmap.h
    struct vm_area_struct *vma;
    struct anon_vma *anon_vma;
    struct list_head same_vma;    /* locked by mmap_sem & page_table_lock */
    struct rb_node rb;            /* locked by anon_vma->rwsem */
    unsigned long rb_subtree_last;
#ifdef CONFIG_DEBUG_VM_RB //默认不使能
    unsigned long cached_vma_start, cached_vma_last;
#endif
};

struct anon_vma_chain 是“双向关系节点”,是连接件,一头连 VMA,一头连 anon_vma,同时被两套索引复用:在 vma->anon_vma_chain 链表里,按 “这个 VMA 关联了哪些 anon_vma” 来看; 在 anon_vma->rb_root 区间树里,按 “这个 anon_vma 关联了哪些 VMA” 来看。解决的是“VMA 和 anon_vma 之间的多对多关系怎么落地。


2.1 成员介绍:

vma:

含义:这条关系对应的那个 VMA。从 anon_vma 侧遍历到 avc 后,可以反推出具体是哪个 VMA。
使用场景:rmap walk 命中某个 avc 后,会通过 avc->vma 拿到 VMA,再去计算地址、清 PTE、回收页等。


anon_vma:

含义:这条关系对应的那个 anon_vma。从 VMA 侧遍历 vma->anon_vma_chain 时,知道这个 VMA 当前关联到了哪些 anon_vma。
典型场景:anon_vma_clone() 把源 VMA 的多个 anon_vma 关系复制到目标 VMA 上,复制的就是一批 avc 节点。


same_vma:

含义:通过此成员将 AVC 挂到 VMA 的 vma->anon_vma_chain 链表上。这个名字很形象:same_vma = “同属一个 VMA 的那些关系节点”。
用途:从 VMA 侧枚举所有关联 anon_vma; 在 split、merge、fork、unlink 时,批量处理这些关系;


rb:

含义:通过此成员将 AVC 挂进 anon_vma->rb_root 区间树。也就是:同一个 AVC,既在 vma->anon_vma_chain 链表里,又在某个 anon_vma 的区间树里。
用途:从 anon_vma 侧按页偏移快速搜索相关 VMA。


rb_subtree_last:

含义:这个 rb 节点所在子树覆盖到的最大页偏移末端。是 interval tree 的增强字段。
具体说:一个 AVC 对应的 VMA 有自己的 [start_pgoff, last_pgoff], 子树里所有节点的 last_pgoff 最大值,会缓存到这个字段中。
用途:查找一个页偏移范围时,可以快速剪枝:如果某个子树的 rb_subtree_last 都小于待查起点,就不用进这棵子树了。


cached_vma_start/cached_vma_last:

仅在 CONFIG_DEBUG_VM_RB 下存在。含义:缓存这个 AVC 对应的 VMA 区间边界,调试校验用。
作用:在 interval tree 插入/删除/更新时做一致性检查,防止 VMA 已改边界,但树里的区间信息没同步更新。


2.1 补充介绍:

(1) 为什么一个 VMA 可能有多个 anon_vma_chain: 因为 fork、split、merge、COW 之后,一个 VMA 可能要保留多条匿名页血缘关系,否则某些旧页在做 rmap 时会找不到它。

(2) fork 建"血缘关系网"(avc 克隆 + 可能新主 anon_vma),COW 改"页归属方向"(新页落到更私有的 anon_vma).


3. struct page_vma_mapped_walk

struct page_vma_mapped_walk {
    struct page *page;
    struct vm_area_struct *vma;
    unsigned long address;
    pmd_t *pmd;
    pte_t *pte;
    spinlock_t *ptl;
    unsigned int flags;
};

page_vma_mapped_walk 是“在某个 VMA 里查某个 page 是否被映射、并迭代所有对应页表项”的上下文对象。它既放“输入参数”,也放“遍历过程中的游标和锁状态”。

 

3.1 成员介绍:

page:

含义:要检查/遍历映射关系的目标物理页(struct page)。遍历时会把当前页表项里的 PFN 与这个 page 对应 PFN 做匹配,判断“这个 PTE/PMD 是否真的映射到它”。
典型来源:rmap 路径里拿到的 page,比如回收、迁移、mprotect/mkclean、memory failure 等路径。
注意点:在 THP 场景里,匹配逻辑通常会考虑大页映射(PMD 映射)或拆分后的 PTE 映射。


vma:

含义:限定查找范围的目标 VMA。page_vma_mapped_walk 不会全进程乱扫,只在这个 VMA 对应的页表范围内查。
约束:address 必须落在这个 VMA 的地址区间里,否则就是无效输入或直接查不到。
常见来源:rmap 已经先定位到某个候选 VMA,再用该结构体做“精查”。


address:

含义:当前正在检查的用户虚拟地址(walk 游标)。角色是双重的:初始化时是输入起点(通常是该 page 在这个 VMA 中推导出的虚拟地址)。遍历过程中会被推进,表示下一次要查的位置。
典型推导思路(文件/匿名页都类似):先把 page 映射到 VMA 的 pgoff 对齐关系。再换算成 VMA 内地址偏移,得到 address 起点。在 PTE-mapped THP 等场景,address 可能被函数内部调整以继续扫描后续 PTE。


pmd:

含义:当前命中的 PMD 级页表项指针(如果当前命中在 PMD 层)。表示 walk 已经走到 PMD 层并锁定了对应上下文。结合 pte 是否为空,可区分“PMD 大页映射”还是“继续到 PTE 层”。
典型状态:开始前应为 NULL, 命中后可能非空, 结束或 restart 后会被清理/重置。


pte:

含义:当前命中的 PTE 指针(如果命中在 PTE 层)。让调用方直接读取或改写这个具体页表项(如清脏、写保护、换 migration entry)。
典型状态机:开始前 NULL。每次成功命中一个映射,pte 指向当前项。跨页表边界或遍历结束时会 unmap 并置回 NULL。


ptl:

含义:保护当前 pte/pmd 的页表锁(page table lock)指针。保证在读取/修改对应页表项时并发安全。
语义上很关键:当 page_vma_mapped_walk 返回命中时,通常 ptl 处于已持有状态。调用方如果提前退出,必须调用对应 done/restart 清理接口释放锁。如果忘记清理,后果就是锁泄漏或后续并发问题。


flags:

含义:控制遍历行为与匹配策略的标志位。
常见用途:选择更严格的同步查找路径(例如避免无锁快路径的不确定性)。指示是否接受 migration entry 这类特殊页表项作为“匹配”。记录遍历中是否跨了页表边界等状态(某些分支会设置状态位给调用方判断)。


3.2 补充说明

(1) 从生命周期视角记成员关系:

初始化阶段(调用前), 填 page、vma、address、flags, 保证 pmd/pte/ptl 为 NULL。单步命中阶段(函数返回 true), address 指向当前命中的映射地址, pmd/pte 至少有一个有效(取决于命中层级), ptl 已持有,可安全检查/修改页表项。继续遍历或收尾,若继续找下一个映射,再次调用 walk。若提前停止,调用 done/restart 释放 ptl 和 pte 映射状态。walk 返回 false 后通常已完成清理,但调用方仍应遵循该接口约定。

实用的一句话记忆:
page 是“我要找谁”,vma+address 是“去哪里找”,pmd/pte 是“当前找到的页表项”,ptl 是“保护这次查看/修改的锁”,flags 是“按什么规则找”。


4. rmap_walk_control

struct rmap_walk_control { //rmap.h
    void *arg;
    struct vm_area_struct *target_vma;
    bool (*rmap_one)(struct page *page, struct vm_area_struct *vma, unsigned long addr, void *arg);
    int (*done)(struct page *page);
    struct anon_vma *(*anon_lock)(struct page *page);
    bool (*invalid_vma)(struct vm_area_struct *vma, void *arg);
};

这个结构体可以看成 rmap 框架的“策略与回调表”。
rmap_walk 本身只负责遍历“这页可能映射到哪些 VMA”,而真正要做什么(清映射、统计引用、跳过哪些 VMA、何时提前停止)都由 struct rmap_walk_control 决定。

可以把整个流程理解成:invalid_vma 先筛候选 -> rmap_one 做实际处理 -> done 决定是否提前收工,而 anon_lock 负责匿名页路径的并发入口。


4.1 成员介绍

rmap_one:

核心处理回调,rmap 遍历每命中一个“page 在某个 vma 的某个地址 addr 的映射”,就调用一次它。用于清/改 PTE、统计 referenced、try_to_unmap、写保护等。返回 true 表示继续遍历下一个映射,false 表示提前停止。


done:

遍历过程中的“提前结束判定”回调。避免无意义继续扫描,比如目标已达成、页已不再需要继续查。返回非 0 表示“可以结束”,0 表示“继续”。

 

anon_lock:

给匿名页路径提供“如何拿 anon_vma(及其锁)”的自定义策略。目的是把“锁策略”从通用遍历逻辑中抽离出来,允许优化或定制。
典型行为:返回可用于后续匿名 rmap 遍历的 anon_vma;失败可返回 NULL 让上层走失败/降级路径。

 

invalid_vma:

VMA 过滤回调。在进入重扫描前快速跳过“不关心/不合法”的 VMA,减少开销。返回 true 表示这个 VMA 无效要跳过,false 表示继续处理该 VMA。

此结构中的前两个非函数成员,可用于把回调串起来:

arg:透传给 rmap_one/invalid_vma 的私有上下文。

target_vma:可选“只关注某个 VMA”的限定条件(是否使用取决于具体实现分支)。

 


三、接口函数

 


四、实现原理

 


五、调试接口

 


六、实验

 

 

七、总结

 

posted on 2026-04-25 15:53  Hello-World3  阅读(7)  评论(0)    收藏  举报

导航