反向映射的应用场景

内核中经常有通过struct page数据结构找到所有映射这个page的VMA的需求 。 早期的Linux内核的实现通过扫描所有进程的VMA , 这种方法相当耗时 。 在Linux2.5开发期间 , 反向映射的概念已经形成 , 经过多年的优化形成现在的版本 。
反向映射的典型应用场景如下 。

  • kswapd 内核线程回收页面需要断开所有映射了该匿名页面的用户PTE页表项 。
  • 页面迁移时 , 需要断开所有映射到匿名页面的用户PTE页表项 。
反向映射的核心函数是try_to_unmap() , 内核中的其他模块会调用此函数来断开一个页面的所有映射 。
int try_to_unmap(struct page *page, enum ttu_flags flags) {int ret;struct rmap_walk_control rwc = {.rmap_one = try_to_unmap_one,.arg = (void *)flags,.done = page_not_mapped,.anon_lock = page_lock_anon_vma_read,};VM_BUG_ON_PAGE(!PageHuge(page)?if ((flags?ret = rmap_walk(page,?if (ret != SWAP_MLOCKreturn ret; }try_to_unmap()函数返回值如下 。
  • SWAP_SUCCESS:成功解除了所有映射的pte 。
  • SWAP_AGAIN:可能错过了一个映射的pte , 需要重新来一次 。
  • SWAP_FAIL:失败 。
  • SWAP_MLOCK:页面被锁住了 。
内核中有3种页面需要unmap操作 , 即KSM页面、匿名页面和文件映射页面 , 因此定义一个rmap_walk_control 控制数据结构来统一管理unmap操作 。
struct rmap_walk_control {void *arg;int (*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); };struct rmap_walk_control 数据结构定义了一些函数指针 , 其中 , rmap_one表示具体断开某个VMA上映射的pte , done表示判断一个页面是否断开成功的条件 , anon_lock实现一个锁机制 , invalid_vma表示跳过无效的VMA 。
[try_to_unmap()->rmap_walk()->rmap_walk_anon()] static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc) {struct anon_vma *anon_vma;pgoff_t pgoff;struct anon_vma_chain *avc;int ret = SWAP_AGAIN; ?anon_vma = rmap_walk_anon_lock(page, rwc);if (!anon_vma)return ret; ?pgoff = page_to_pgoff(page);anon_vma_interval_tree_foreach(avc,unsigned long address = vma_address(page, vma); ?if (rwc->invalid_vma?ret = rwc->rmap_one(page, vma, address, rwc->arg);if (ret != SWAP_AGAIN)break;if (rwc->done}anon_vma_unlock_read(anon_vma);return ret; }【反向映射的应用场景】第7行代码 , rmap_walk_anon_lock()获取页面page->mapping 指向的anon_vma数据结构 , 并申请一个读者锁 。 第12行代码 , 遍历anon_vma->rb_root红黑树中的avc , 从avc中可以得到相应的VMA , 然后调用rmap_one()来完成断开用户PTE页表项 。