分页机制虽然很灵活,但您也看到了,为了实现虚拟地址到物理地址的映射,过程还是有些麻烦的。先要从CR3寄存器中获取页目录表物理地址,然后用虚拟地址的高10位乘以4的积做为在页目录表中的偏移量去寻址目录项pde,从pde中读出页表物理地址,然后再用虚拟地址的中间10位乘以4的积做为在该页表中的偏移量去寻址页表项pte,从该pte中读出页框物理地址,用虚拟地址的低12位做为该物理页框的偏移量,呼…终于完成虚拟地址到物理地址的映射。
每一个虚拟地址到物理地址的转换都要重复以上过程,甭说真正去做了,光描述这个过程我都觉得繁琐,何况这只是用二级页表做地址映射的过程,要是用三级页表…我都替处理器喊累。不止如此,处理器的速度和内存的速度完全是两个数量级,页表毕竟在内存中,转换过程中频繁的内存访问,使得地址转换速度慢上加慢,而处理器也不得不停下来等待内存的响应。
虚拟地址到物理地址的转换,最终是想得到虚拟地址所对应的物理地址,如果给出一个虚拟地址后能直接得到相应的页框物理地址,免去中间的查表过程,直接用虚拟地址的低12位在该物理页框中寻址,岂不是大大提高了地址转换速度。根据程序的局部性原理,可以将近来常用的地址和指令加载到速度更快的设备中,因此我们都想到了缓存。处理器准备了一个高速缓存,可以匹配高速的处理器速率和低速的内存访问速度,它专门用来存放虚拟地址页框与物理地址页框的映射关系,这个调整缓存就是TLB,即Translation Lookaside Buffer,俗称快表,其结构如图
TLB中的条目是虚拟地址的高20位到物理地址高20位的映射结果,实际上就是从虚拟页框到物理页框的映射。除此之外TLB中还有一些属性位,比如页表项的RW属性。
有了TLB,处理器在寻址之前会用虚拟地址的高20位做为索引来查找TLB中的相关条目,如果命中(匹配到相关条目)则返回虚拟地址所映射的物理页框地址,否则会查询内存中的页表,获得页框物理地址后再更新TLB。
高速缓存由于成本等原因,容量一般都很小,TLB也是,因此TLB中的数据只是当前任务的部分页表,而且只有P位为1的页表项才有资格在TLB中,如果TLB被装满了,需要将很少使用的条目换出。
缓存相当于数据源的快照,为了保证缓存与数据源同步变化,这就涉及到缓存刷新的问题。TLB也是缓存,当内存中的原页表被修改时,TLB中的相应映射关系按理说也要更新。一般的缓存可以定期刷新,甚至推迟几分钟都可以,但TLB和一般的缓存可不一样,您想,TLB是页表的缓存,处理器寻址时最先访问的是TLB,TLB里面存储的是程序运行所依赖的指令和数据的内存地址,任意时刻都必须保证地址的有效性,否则程序必然出错,所以TLB必须实时更新。可是如果实时读取内存中的页表去更新TLB的话,这又回到了从内存查询映射关系的老路,TLB反而成了鸡肋。为此,TLB并不自动更新,处理器也不负责TLB的有效性,它把TLB的维护工作交给操作系统开发人员,由开发人员手动控制。这的确是非常合理的,毕竟维护页表的代码是开发人员自己写的,他们肯定知道何时修改了页表,或是修改了哪些条目。
尽管TLB对开发人员不可见,但依然有两种方法可以间接更新TLB,一个是针对TLB中所有条目的方法——重新加载CR3,比如将CR3寄存器的数据读出来后再写入CR3,这会使整个TLB失效。另一个方法是针对TLB中某个条目的更新。处理器提供了指令invlpg(invalidate page),它用于在TLB中刷新某个虚拟地址对应的条目,处理器是用虚拟地址来检索TLB的,因此很自然地,指令invlpg的操作数也是虚拟地址,其指令格式为invlpg m。注意,其中m表示操作数为虚拟内存地址,并不是立即数,比如要更新虚拟地址0x1234对应的条目,指令为invlpg [0x1234],并不是invlpg 0x1234。将来咱们在内存管理系统中会涉及到TLB的更新操作,这一点尤为注意。
好啦,有关TLB的介绍就到这儿,下节再见