持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
用户进程在使用虚拟内存的过程中,从虚拟内存页面映射到物理内存页面时,PTE保留这个记录,page数据结构中的_mapcount记录有多少个用户PTE映射到物理页面。用户PTE是指用户进程地址空间和物理页面建立映射的PTE,不包括内核地址空间映射物理页面时产生的PTE。有的页面需要迁移,有的页面长时间不使用,需要交换到磁盘。在交换之前,必须找出哪些进程使用这个页面,然后解除这些映射的用户PTE。一个物理页面可以同时被多个进程的虚拟内存映射,但是一个虚拟页面同时只能映射到一个物理页面。 在Linux 2.4内核中,为了确定某一个页面是否被某个进程映射,必须遍历每个进程的页表,因此工作量相当大,效率很低。在Linux 2.5内核开发期间,提出了反向映射(Reverse Mapping,RMAP)的概念。
RMAP的主要数据结构
RMAP的主要目的是从物理页面的page数据结构中找到有哪些映射的用户PTE,这样页面回收模块就可以很快速和高效地把这个物理页面映射的所有用户PTE都解除并回收这个页面。 为了达到这个目的,内核在页面创建时需要建立RMAP的“钩子”,即建立相关的数据结构。RMAP系统中有两个重要的数据结构:一个是anon_vma,简称AV;另一个是anon_vma_chain,简称AVC。
1.anon_vma数据结构
anon_vma数据结构主要用于连接物理页面的page数据结构和VMA的vm_area_struct数据结构。VMA的数据结构中有一个成员指向anon_vma数据结构。
struct vm_area_struct {
...
struct anon_vma *anon_vma;
...
};
另外,page数据结构中的mapping成员指向匿名页面的anon_vma数据结构
2、父进程产生匿名页面
父进程为自己的进程地址空间VMA分配物理内存时,通常会产生匿名页面。例如,缺页异常处理中的do_anonymous_page()函数会产生匿名页面,通过do_wp_page()处理写时复制时也会产生一个新的匿名页面。以do_anonymous_page()产生一个新的匿名页面为例。
<用户态malloc()分配虚拟内存→用户进程写内存→内核发生缺页异常→ do_anonymous_page()>
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)
{
...
anon_vma_prepare(vma);
page = alloc_zeroed_user_highpage_movable(vma, address);
...
page_add_new_anon_rmap(page, vma, address);
...
}
在产生匿名页面时,调用RMAP系统的两个接口函数来完成初始化,一个是anon_vma_prepare()函数,另一个是page_add_new_anon_rmap()函数。 下面来看anon_vma_prepare()函数的实现。