所谓反向映射是相对于从虚拟地址到物理地址的映射,反向映射是从物理页面到虚拟地址空间VMA的反向映射。
RMAP能否实现的基础是通过struct anon_vma、struct anon_vma_chain和sturct vm_area_struct建立了联系,通过物理页面反向查找到VMA。
用户在使用虚拟内存过程中,PTE页表项中保留着虚拟内存页面映射到物理内存页面的记录。
一个物理页面可以同时被多个进程的虚拟地址内存映射,但一个虚拟页面同时只能有一个物理页面与之映射。
不同虚拟页面同时映射到同一物理页面是因为子进程克隆父进程VMA,和KSM机制的存在。
如果页面要被回收,就必须要找出哪些进程在使用这个页面,然后断开这些虚拟地址到物理页面的映射。
匿名页面实际的断开映射操作在rmap_walk_anon中进行的,可以看出从struct page、到struct anon_vma、到struct anon_vma_chain、到struct vm_area_struct的关系。
1. 父进程分配匿名页面
父进程为自己的进程地址空间VMA分配物理内存时,通常会产生匿名页面。
do_anonymous_page()会分配匿名页面;do_wp_page()发生写时复制COW时也会产生一个新的匿名页面。
static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, pte_t *page_table, pmd_t *pmd,
unsigned int flags)
{
...
/* Allocate our own private page. */
if (unlikely(anon_vma_prepare(vma)))------------------------------为进程地址空间准备struct anon_vma数据结构和struct anon_vma_chain链表。
goto oom;
page = alloc_zeroed_user_highpage_movable(vma, address);----------从HIGHMEM区域分配一个zeroed页面
if (!page)
goto oom;
...
inc_mm_counter_fast(mm, MM_ANONPAGES);
page_add_new_anon_rmap(page, vma, address);-----------------------
mem_cgroup_commit_charge(page, memcg, false);
lru_cache_add_active_or_unevictable(page, vma);
...
}
RMAP反向映射系统中有两个重要的数据结构:一个是struct anon_vma,简称AV;一个是struct anon_vma_chain,简称AVC。
struct anon_vma {
struct anon_vma *root; /* Root of this anon_vma tree */----------------指向anon_vma数据机构中的根节点
struct rw_semaphore rwsem; /* W: modification, R: walking the list */------保护anon_vma中链表的读写信号量
/*
* The refcount is taken on an anon_vma when there is no
* guarantee that the vma of page tables will exist for
* the duration of the operation. A caller that takes
* the reference is responsible for clearing up the
* anon_vma if they are the last user on release
*/
atomic_t refcount;------------------------------------------------------------对anon_vma的引用计数
/*
* Count of child anon_vmas and VMAs which points to this anon_vma.
*
* This counter is used for making decision about reusing anon_vma
* instead of forking new one. See comments in function anon_vma_clone.
*/
unsigned degree;
struct anon_vma *parent; /* Parent of this anon_vma */--------------------指向父anon_vma数据结构
/*
* NOTE: the LSB of the rb_root.rb_node is set by
* mm_take_all_locks() _after_ taking the above lock. So the
* rb_root must only be read/written after taking the above lock
* to be sure to see a valid next pointer. The LSB bit itself
* is serialized by a system wide lock only visible to
* mm_take_all_locks() (mm_all_locks_mutex).
*/
struct rb_root rb_root; /* Interval tree of private "related" vmas */-----红黑树根节点
}
struct anon_vma_chain数据结构是链接父子进程的枢纽:
struct anon_vma_chain {
struct vm_area_struct *vma;-----------------------------------------------指向VMA
struct anon_vma *anon_vma;------------------------------------------------指向anon_vma数据结构,可