反向映射
1 | struct page{ |
匿名页经常由几个进程共享。
最常见的情形:创建新进程,父进程的所有页框,包括匿名页,同时也分配给子进程。
另外(不常见),进程创建线性区时使用两个标志 MAP_ANONYMOUS 和 MAP_SHARED,表明这个区域内的也将由该进程后面的子进程共享。
将引用同一页框的所有匿名页链接起来的策略:将该页框所在的匿名线性区存放在一个双向循环链表中。
注意:即使一个匿名线性区存有不同的页,也始终只有一个反向映射链表用于该区域中的所有页框。
当为一个匿名线性区分配第一页时,内核创建一个新的 anon_vma 数据结构,它只有两个字段:
- lock,竞争条件下保护链表的自旋锁。
- head,线性区描述符双向循环链表的头部。
然后,内核将匿名线性区的 vm_area_struct 描述符插入 anon_vma 链表。vm_area_struct 中对应链表的两个字段:
- anon_vma_node,存放指向链表中前一个和后一个元素的指针。(多个vm_area_struct链接到一起)
- anon_vma,指向 anon_vma 数据结构。
最后,内核将 anon_vma 数据结构的地址存放在匿名页描述符的 mapping 字段。
当已被一个进程引用的页框插入另一个进程的页表项时(如调用 fork() 时),内核只是将第二个进程的匿名线性区插入 anon_vma 数据结构的双向循环链表,而第一个进程线性区的 anon_vma 字段指向该 anon_vma 数据结构。
因此,每个 anon_vma 链表通常包含不同进程的线性区。
借助 anon_vma 链表,内核可快速定位引用同一匿名页框的所有页表项。
每个区域描述符在 vm_mm 字段中存放内存描述符地址,而该内存描述符又有一个 pgd 字段,其中存有进程的页全局目录。
这样,页表项就可以从匿名页的起始线性地址得到,该线性地址可以由线性区描述符及页描述符的 index 字段得到。