反向映射一直是提高pageout效率的一个热门话题。在2.6内核中,在page结构中加入了一个链表指针,保存了所有引用了该页的映射(pte chains)。虽然这能很好的实现反向映射,但是却在映射/解映射、fork/exit时花费了太多的空间和时间。
因此出现了一种新的反向映射方法——基于对象的反向映射。
在2.4内核之前,内核是不提供物理内存地址到虚拟地址的映射机制的(就是我们所说的反向映射)。这就意味着,当我们要换出一个物理页的时候,我们无法将所有指向该物理页的指针置空。除非你遍历所有进程的页表,对相关的指针进行处理。当所有指向该物理页的引用都被解除后才能将该物理页作为换出页。当然,这将耗费我们大量的时间,如果一个页在将要被换出的时候能马上知道哪些页表引用了它,这将非常高效。因此,我们需要反向映射。
后来两个内核开发者实现了pte chains,就是在page结构中加入一个链表,将所有引用该页的映射链起来。这使得我们执行fork、exec或exit时开销很大。因为开销和该进程映射的数目成正比。第二个不足就是花费了太多的内存空间。
我们注意到进程通常不是一页一页的映射数据,而是一个区间一个区间的映射。每个page结构里都会有一个指向address_space结构的指针,而address_space结构则是进程与文件关联的中间层。文件中的每段映射,内核都会用一个vma结构来表示,而vma包含了映射的虚拟地址,以及在文件内的偏移量。之后vma结构会被添加到address_space结构的vma链表中。
而反向映射中真正的问题在于匿名映射。因为匿名映射的vma并不包含共同的结构体,类似于address_space用于反向映射。因此又为每个进程添加了anonmm结构,page结构里存储了该页的虚拟地址偏移量并会通过指针指向其所属的anonmm结构。因此可通过该结构来进行反向映射。
但这个实现忽略了共享匿名映射。因为共享匿名映射的页允许被多个进程引用,因此也会出现在多个进程的地址空间中。最后,又增加了一个anon_vma结构指向匿名映射的vma结构。该结构会维护一个链表,包含了所有映射了该段区域的匿名映射。之后,又对其进行了改进。即将每个匿名映射都设置成copy-on-write。这样就没有共享匿名映射的问题了,但是稍微花费了一些空间。