虚拟存储器:TLB设计
TLB基本设计
对于一个两级页表,我们需要访问两次物理内存才能得到相应的物理地址。访问物理内存是一个耗时的过程,因此在这里引入cache的设计理念,将页表中最近使用的PTE缓存下来。
我们将缓存PTE的部件称为TLB,TLB就是页表的Cache。但是对于页表而言,当前访问的页很有可能被再次访问,但是相邻的页并没有绝对相关性。因此TLB只能利用到cache事件相关性的设计理念。
TLB的设计对采用全相联的方式,降低了组缺失率。组相联的设计要求TLB的容量不能过大(或许会导致比较器过于复杂)。为了实现更大容量的TLB也可以使用组相联的设计。现代处理器中通常使用两级TLB,其中第一级分为D-TLB与I-TLB使用全相联设计,而第二级TLB则可以使用组相联的设计。
TLB的内容包括:
- Cacheable
- AP:访问权限
- V:有效位
- D:脏位
- U:使用次数
- Tag:记录VPN,进行匹配查找
- PFN:VPN与PFN的转换,方便进行写回是的寻址?
对于4KB大小的页,是无法满足现代动辄数MB的程序的需求的,因此需要有更大容量的页。但是对于大容量的页,也诞生了新的问题,对于多数情况,一个页内大部分的空间是浪费的,同时对于Page Fault的处理,更大的页意味着要用更长的时间才能将下级存储内的数据移到物理内存中。
为了解决这种问题,可以使用容量可变的页,可以通过在TLB中增加一个size位与操作系统的自动分配算法进行共同管理。对于不同大小的页,寻址也需要进行不同的处理,其中Tag的长度会发生变化。
TLB所有的项除了脏位和使用位之外,其余的项在TLB中都是不会改变的,因此写回的时候也只要考虑这两项的写回。
TLB缺失
TLB缺失的情况可以分为以下几种:
- 虚拟地址对应的页不在物理内存中。
- 虚拟地址对应的页在物理内存中,但还没被访问过或者被替换了
解决TLB缺失就要找到虚拟地址对应的页并将它写入TLB,这个过程称为Page Table Walk。有两种解决方案
软件实现
软件实现的好处总是灵活性更高。需要使用一个特殊寄存器存下虚拟地址,在通过使用一些直接操作TLB的指令,然后逐级查找PT找到对应的PTE。在这个过程中,需要中断当前的处理器中的指令序列,同时为了避免在这个过程中再吃出现TLB缺失的问题,需要将这段程序直接放在一个不需要进行地址转换的物理内存中(与操作系统在一起)
硬件实现
直接由MMU实现使用一个状态机来实现,这样的性能会更好,但相比于软件实现不能完成更加灵活的替换策略。
两种方法相比,各有优势。软件实现需要实现的硬件复杂度更低,并且替换策略更加灵活。硬件实现由于不需要修改流水线的进程,只需要要STALL,而软件在处理完缺失后要将之前的指令重新写入(流水线较深时耗费的时间会更加明显),因此硬件实现花费的时间反而更短。
TLB的写入
对于store指令,由于使用了TLB,因此现在虚拟地址首先会访问TLB,如果命中,那么TLB中的脏位和使用位都应该被设置为 ,如果采用写回的策略也就是当这个表项要被替换或修改时才写回到页表中。
但是写回的方式也存在新的问题,对于发生Page Fault时,当从硬盘中找到虚拟地址对应的页并对于页表中的进行替换时,此时由于我们没有采用写通的策略,页表中的使用位和脏位可能都是过时的,这会导致我们的替换发生问题。要解决这个问题,我们就要再进行替换之前首先检查TLB,将TLB中的内容其全部写回,可以想象这是一个低效的过程,出现了很多不必要的写回。
实际上,我们可以通过一个更加简单的方式来完成这个问题,由于TLB的容量是很有限的,因此可以认为所有被TLB记录的页都是重要的,这样我们只需要在页表中增加一位来记录当前页是否有被放入TLB中,对于所有TLB记录的页不进行替换,这也是我们替换策略的一部分。也就是说TLB中记录的页不允许被从页表中替换
同样的也可以引出另一个问题,物理内存中的内容可能也是过时的,在替换时需要考虑的Cache的存在,这个问题之后再考虑。
TLB的控制
正常情况下,TLB是页表的一个子集,并且根据上面的替换策略,发生Page Fault时,也不会影响记录在TLB中的页。当发生TLB的缺失时,直接执行相关的替换策略就可以了。但是由于操作系统在一些情况下会将页表中的一些页删除,此时如果TLB中也记录了同样的页,TLB就需要和页表保持一致,如一个进程结束了需要页表和TLB都置为无效,以便后续的进程使用。
简单来说,对于TLB的控制需要简单实现三个基本的操作:
- 重置TLB所有的表项
- 重置某个进程(ASID)的表项
- 重置某个VPN对应的表项
对于不同的处理器,有着不同的方式来实现相应的功能。