点我查看操作系统秘籍连载


页翻译:快速地址转换

虽然操作系统通过页表也能将虚拟页翻译成内存中对应的页帧,但是它仍然很慢。另一方面,如果访问每个页都需要操作系统来参与帮忙翻译,这会频繁陷入内核,效率是非常低的。所以,这里再次将任务交给硬件CPU去做。

提示:操作系统将底层任务交给硬件提高效率

前文介绍段的虚拟地址翻译,以及这里介绍的页翻译,本都可以由操作系统完成,但是操作系统参与太多效率会非常低,这时候都将任务交给操作系统的好伙伴——硬件(CPU)来完成,这会减少大量的上下文切换,因为不用再陷入内核了。

不仅如此,磁盘IO本也是可以由操作系统参与完成的,但速度会更慢,所以也将IO任务交给了硬件(硬盘)去完成。此外,还有网卡、显卡等等。

所以可以做个总结,只要频繁进行底层操作的任务,一般都会交给硬件而绕过操作系统内核(因为绕过内核,这类任务也常称为内核旁路操作),它们的原理都一样。而要交给硬件,硬件肯定要支持这类操作。

CPU对页做快速地址转换,其全称为translation-lookaside buffer(TLB),即地址转换旁路缓冲。快速地址转换之所以称为TLB,这是历史原因造成的。但是否注意到旁路缓冲这几个字?即绕过内核的缓冲,也就是硬件中的缓冲。

所以,CPU对页做快速地址转换是借助CPU的高速缓冲区完成的,而且使用的L1级缓存(离CPU核心最近的缓存)和L2级缓存(比L1缓存稍慢),翻译速度可想而知有多快。

这里以最简单的方式描述下快速地址转换涉及到的一些过程。

虽然CPU的高速缓存速度非常快,但与之对应的是缓存空间非常小,所以只能保存有限数量的翻译信息。如果高速缓冲区缓存的翻译信息数量已满,当访问缓存中不存在翻译信息的页,就只能从内存的多级页表中读取翻译信息,因为这条翻译信息也要缓存到高速缓冲区,所以只能从高速缓冲区中踢掉之前的一条翻译信息。那么踢掉哪一项呢?于是缓存替换策略算法就派上用场了,例如随机踢掉一项的随机替换算法,踢掉最近最少访问的那项的LRU算法,等等。

这里给一个访问数组的示例,来帮助理解TLB带来的好处。如下图是数组a中10个元素的存放方式,总共存放在3个虚拟页中。

如果依次去访问这10个数组元素,首先访问a[0]时高速缓冲区中没有缓存相关的翻译信息,所以需要从内存中去读取VPN 02页的翻译信息并缓存在高速缓冲区中,然后到对应的物理内存中去取得该元素的值。但是当访问a[1]和a[2]的时候就能够从高速缓冲区中受益,因为这两个元素都在VPN 02页中,而该页的翻译信息已经缓存了。

同理,访问新页中a[03]和a[7]的时候,需要先从内存中读取页VPN 03和VPN 04的翻译信息并缓存下来,之后再访问页中其它元素的时候就可以直接从缓存中取得翻译信息,直接找到物理内存中的值。

这里可以做个假设,假如每页的大小足够大,数组a中的元素可以全部存放在同一个页中,那么缓存的效果显然更好。但页过大,容易造成大量的页空间浪费,因为内存的操作单元是页,如果一个100K的页只存放了1K的数据,也只能读取这100K,并且剩余99K空间被浪费,这种浪费称为内部碎片

提示:空间的外部碎片和内部碎片

这是空间管理的两个经常出现概念。

内部碎片是给空间按固定大小划分成块或页后,块内或页内的空间没有完全利用导致有一部分空间浪费,这种称为内部碎片,即页内或块内的空间碎片。一般使用填充因子或页密度来描述页的空间使用率是高还是低。内部碎片有好处也有坏处,坏处是浪费了一点空间,但这是无法避免的,好处是每页都留下了空闲空间,如果程序支持的话(例如数据库),以后该页可以继续存入新数据。

外部碎片是因为给空间划分了不同大小的区段(即分段或分区)后,随着空间的分配和释放,导致空间的不连续(即出现了空间孔洞)。例如3段连续空间大小分别为10K、20K、10K,如果中间的20K已被使用而左右两个10K是空闲的(这是可能的,空间可以在使用后释放),即使现在总共有20K空闲空间,但如果想要申请15K空间,结果要么失败,要么通过链表将不连续的空间链起来,不同程序处理方式不一样。所以,外部碎片过多的负面影响非常大,要么因无法分配空间导致程序异常终止,要么因为访问空间时要通过链表跳转的重定位而严重影响性能。