3.3.4 针对大内存的页表

在原有的内存页表的方案之上,引入快表(TLB)可以用来加快虚拟地址到物理地址的转换。不过这不是惟一需要解决的问题,另一个问题是怎样处理巨大的虚拟地址空间。下面将讨论两种解决方法。

1.多级页表

第一种方法是采用多级页表。一个简单的例子如图3-13所示。在图3-13a中,32位的虚拟地址被划分为10位的PT1域、10位的PT2域和12位的Offset(偏移量)域。因为偏移量是12位,所以页面长度是4KB,共有220 个页面。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

引入多级页表的原因是避免把全部页表一直保存在内存中。特别是那些从不需要的页表就不应该保留。比如一个需要12MB内存的进程,其最底端是4MB的程序正文段,后面是4MB的数据段,顶端是4MB的堆栈段,在数据段上方和堆栈段下方之间是大量根本没有使用的空闲区。

考察图3-13b例子中的二级页表是如何工作的。在左边是顶级页表,它具有1024个表项,对应于10位的PT1域。当一个虚拟地址被送到MMU时,MMU首先提取PT1域并把该值作为访问顶级页表的索引。因为整个4GB(32位)虚拟地址空间已经被分成1024个4MB的块,所以这1024个表项中的每一个都表示4MB的虚拟地址空间。

阅读 ‧ 电子书库
图 3-13 a)一个有两个页表域的32位地位;b)二级页表

由索引顶级页表得到的表项中含有二级页表的地址或页框号。顶级页表的表项0指向程序正文的页表,表项1指向数据的页表,表项1023指向堆栈的页表,其他的表项(用阴影表示的)未用。现在把PT2域作为访问选定的二级页表的索引,以便找到该虚拟页面的对应页框号。

下面看一个示例,考虑32位虚拟地址0x00403004(十进制4 206 596)位于数据部分12 292字节处。它的虚拟地址对应PT1=1,PT2=2,Offset=4。MMU首先用PT1作为索引访问顶级页表得到表项1,它对应的地址范围是4M~8M。然后,它用PT2作为索引访问刚刚找到的二级页表并得到表项3,它对应的虚拟地址范围是在它的4M块内的12 288~16 383(即绝对地址4 206 592~4 210 687)。这个表项含有虚拟地址0x00403004所在页面的页框号。如果该页面不在内存中,页表项中的“在/不在”位将是0,引发一次缺页中断。如果该页面在内存中,从二级页表中得到的页框号将与偏移量(4)结合形成物理地址。该地址被放到总线上并送到内存中。

值得注意的是,虽然在图3-13中虚拟地址空间超过100万个页面,实际上只需要四个页表:顶级页表以及0~4M(正文段)、4M~8M(数据段)和顶端4M(堆栈段)的二级页表。顶级页表中1021个表项的“在/不在”位都被设为0,当访问它们时强制产生一个缺页中断。如果发生了这种情况,操作系统将注意到进程正在试图访问一个不希望被访问的地址,并采取适当的行动,比如向进程发出一个信号或杀死进程等。在这个例子中的各种长度选择的都是整数,并且选择PT1与PT2等长,但在实际中也可能是其他的值。

图3-13所示的二级页表可扩充为三级、四级或更多级。级别越多,灵活性就越大,但页表超过三级会带来更大的复杂性,这样做是否值得令人怀疑。

2.倒排页表

对32位虚拟地址空间,多级页表可以很好地发挥作用。但是,随着64位计算机变得更加普遍,情况发生了彻底的变化。如果现在的地址空间是264 字节,页面大小为4KB,我们需要一个有252 个表项的页表。如果每一个表项8个字节,那么整个页表就会超过3000万GB(30PB)。仅仅为页表耗费3000万GB不是个好主意(现在不是,可能以后几年也不是)。因而,具有64位分页虚拟地址空间的系统需要一个不同的解决方案。

解决方案之一就是使用倒排页表(inverted page table)。在这种设计中,在实际内存中每一个页框有一个表项,而不是每一个虚拟页面有一个表项。例如,对于64位虚拟地址,4KB的页,1GB的RAM,一个倒排页表仅需要262 144个页表项。表项记录哪一个(进程,虚拟页面)对定位于该页框。

虽然倒排页表节省了大量的空间(至少当虚拟地址空间比物理内存大得多的时候是这样的),但它也有严重的不足:从虚拟地址到物理地址的转换会变得很困难。当进程n访问虚拟页面p时,硬件不再能通过把p当作指向页表的一个索引来查找物理页框。取而代之的是,它必须搜索整个倒排页表来查找某一个表项(n,p)。此外,该搜索必须对每一个内存访问操作都要执行一次,而不仅仅是在发生缺页中断时执行。每一次内存访问操作都要查找一个256K的表是不会让你的机器运行得很快的。

走出这种两难局面的办法是使用TLB。如果TLB能够记录所有频繁使用的页面,地址转换就可能变得像通常的页表一样快。但是,当发生TLB失效时,需要用软件搜索整个倒排页表。一个可行的实现该搜索的方法是建立一张散列表,用虚拟地址来散列。当前所有在内存中的具有相同散列值的虚拟页面被链接在一起,如图3-14所示。如果散列表中的槽数与机器中物理页面数一样多,那么散列表的冲突链的平均长度将会是1个表项,这将会大大提高映射速度。一旦页框号被找到,新的(虚拟页号,物理页框号)对就会被装载到TLB中。

阅读 ‧ 电子书库
图 3-14 传统页表与倒排页表的对比

倒排页表在64位机器中很常见,因为在64位机器中即使使用了大页面,页表项的数量还是很庞大的。例如,对于4MB页面和64位虚拟地址,需要242 个页表项。处理大虚存的其他方法可参见Talluri等人的论文(1995)。