分页:更快转换--(TLBs)

使用分页作为支持虚拟内存的核心机制可以导致高性能的开销。把地址空间切成小的、固定大小的单位(即:分页需要大量的映射信息。因为映射信息通常存储在物理内存中,因此,在逻辑上需要对程序生成的每个虚拟地址进行额外的内存查找。在每次指令取出或显式加载或存储之前都要对翻译信息进行记忆,这是非常缓慢的。因此我们的问题。

  • 症结

如何加快地址转换?我们如何加快地址转换,并且通常避免分页所需要的额外内存引用?需要什么硬件支持?需要什么样的操作系统参与!当我们想让事情快速发展时,操作系统通常需要一些帮助。而帮助往往来自于操作系统的老朋友:硬件。为了加速地址转换,我们将添加所谓的(基于历史原因[CP78])一个平移-lookaside缓冲,或TLB [CG68, C95]。TLB是芯片内存管理单元(MMU)的一部分,它只是流行的虚拟到物理地址转换的硬件缓存;因此,更好的名称应该是地址转换缓存。在每个虚拟内存引用上,硬件首先检查TLB,看看是否需要进行所需的翻译。

19.1 TLB 基本算法

图19.1展示了硬件如何处理虚拟地址转换,假设有一个简单的线性页表(即:,页表是一个数组)和一个硬件管理的TLB(即,硬件处理页面表访问的大部分责任;下面我们将对此进行更多的解释。

分页:更快转换--(TLBs)_第1张图片


TLB控制流算法

分页:更快转换--(TLBs)_第2张图片

算法的硬件之前是这样运作的:第一,提取虚拟页码(VPN)的虚拟地址(1号线在图19.1),并检查如果TLB VPN的翻译(2行)。如果是,我们有一个TLB冲击,这意味着TLB翻译。成功!我们现在可以从相关的TLB条目中提取页面帧数(PFN),将其连接到原始虚拟地址的偏移位置,并形成所需的物理地址(PA)和访问内存(第5行),假设保护检查没有失败(第4行)。

如果CPU在TLB (TLB缺失)中没有找到转换,我们还有一些工作要做。在本例中,硬件访问页表以查找翻译(第11行12行),并且假定进程生成的虚拟内存引用是有效的和可访问的(第13行、第15行),通过翻译更新TLB(第18行)。这些操作的代价很高,主要是因为访问页表所需的额外内存引用(第12行)。最后,一旦TLB被更新,硬件重试指令;这一次,翻译是在TLB中发现的。 

与所有缓存一样,TLB构建在一个前提之上,即在普通情况下,在缓存中找到翻译(即:点击)。如果是这样的话,就会增加一些额外的开销,因为TLB在处理核心附近被发现,并且设计得非常快。当出现失误时,就会产生较高的分页费用;必须访问页表来查找翻译,以及一个额外的内存引用(或更多,具有更复杂的页表)的结果。如果这种情况经常发生,程序可能会明显地慢一些;相对于大多数CPU指令而言,内存访问开销很大,而且TLB会丢失。

分页:更快转换--(TLBs)_第3张图片

19.2示例:访问数组。

为了明确TLB的操作,让我们检查一个简单的虚拟地址跟踪,看看TLB如何提高它的性能。在这个例子中,假设我们在内存中有一个10个4字节的整数数组,从虚拟地址100开始。假设我们有一个8位的虚拟地址空间,有16个字节的页面;因此,虚拟地址分解为4位VPN(有16个虚拟页面)和4位偏移量(每个页面上有16个字节)。

图19.2显示了该系统16个16字节页面上的阵列。可以看到,数组的第一个条目(a[0])开始(VPN=06, offset=04);只有3个4字节的整数符合这个页面。数组继续到下一页(VPN=07),接下来的四个条目(a[3]…[6])。最后,10个条目数组的最后三个条目(a[7]…a[9])位于地址空间的下一页(VPN=08)。

现在让我们考虑一个简单的循环,它访问每个数组元素,在C中是这样的。


为了简单起见,我们将假定只访问该循环的内存访问是对数组(忽略了变量i和sum以及指令本身)。当第一个数组元素(a[0])被访问时,CPU将看到一个加载到虚拟地址100。硬件从这个(VPN=06)中提取VPN,并使用它检查TLB是否为有效的翻译。假设这是程序第一次访问数组,结果将是TLB缺失。下一个访问权限是[1],这里有一个好消息:TLB命中!因为数组的第二个元素被打包在第一个元素的旁边,它就在同一个页面上;因为我们在访问数组的第一个元素时已经访问了这个页面,所以翻译已经加载到TLB中了。这就是我们成功的原因。访问[2]遇到相似的成功(另一个成功),因为它与[0]和[1]在同一页上。不幸的是,当程序访问[3]时,我们会遇到另一个TLB错误,但是,再次,下一个条目(a[4]…a[6])将在TLB中命中,因为它们都驻留在内存中的同一页上。最后,访问一个[7]导致最后一个TLB错误。硬件再次查询页表,以确定这个虚拟页面在物理内存中的位置,并相应地更新TLB。最后两个访问(a[8]和[9])接收到TLB更新的好处;当硬件在TLB中查找它们的翻译时,会产生两个结果。

让我们总结一下十次访问数组中TLB的活动:miss, hit, hit, miss, hit, hit, hit, miss, hit, hit。因此,我们的TLB命中率,即按总访问次数除以总次数的次数,为70%。虽然这不算太高(实际上,我们希望达到100%的命中率),但它是非零的,这可能是一个惊喜。尽管这是程序第一次访问数组,但是TLB由于空间位置而提高了性能。数组的元素被紧密地压缩到页面中(即紧密到页面。它们在空间上彼此靠近,因此。 只有第一次访问页面上的元素才会产生TLB错误。

还要注意页面大小在这个示例中所扮演的角色。如果页面大小仅仅是两倍(32个字节,而不是16个字节),那么数组访问将遭受更少的遗漏。由于典型的页面大小更像4KB,这些类型的密集的、基于数组的访问实现了出色的TLB性能,每次访问只会遇到单个的错误。

关于TLB性能的最后一点:如果程序在这个循环结束后不久,再次访问数组,我们可能会看到一个更好的结果,假设我们有足够大的TLB来缓存所需的翻译:命中、命中、命中、命中、命中、命中、命中、命中、命中、命中。在这种情况下,TLB的命中率会很高,因为时间局部性,即。,快速的重新引用内存项目。与任何缓存一样,TLBs依赖于空间和时间位置的成功,这是程序属性。如果兴趣项目表现出这样的地方(和许多项目)。 做),TLB命中率可能很高。

提示:尽可能使用缓存

高速缓存是计算机系统中最基本的性能技术之一,它可以一次又一次地使用,以使常见的情况快速地发生[HP06]。硬件缓存背后的思想是利用本地的指令和数据引用。通常有两种地方:时间局部性和空间局部性。随着时间的推移,这个想法是最近访问的一个指令或数据项很可能会在不久的将来被重新访问。在循环中考虑循环变量或指令;它们会随着时间不断地被访问。在空间位置上,我们的想法是,如果一个程序在地址x上访问内存,它很可能很快就会访问x附近的内存,想象一下,在这里通过某种数组,访问一个元素,然后再访问一个元素。当然,这些属性取决于程序的确切性质,因此并不是严格的法律,更像是经验法则。

硬件缓存,无论是用于指令、数据还是地址转换(在我们的TLB中),都是利用本地的优势,将内存复制到小的、快速的芯片内存中。处理器可以先检查附近的副本是否存在于缓存中,而不必去(缓慢的)内存来满足请求。如果是这样,处理器可以快速访问它。在一些CPU周期中,避免花费大量的时间访问内存(许多纳秒)。

您可能想知道:如果缓存(像TLB)那么好,为什么我们只做更大的缓存并保留所有的数据呢?不幸的是,这是我们遇到更基本定律的地方,就像物理定律一样。如果你想要高速缓存,它必须很小,因为像光速和其他物理约束这样的问题变得很重要。根据定义,任何大型缓存都是缓慢的,因而无法达到目的。因此,我们被困在小型高速缓存中;剩下的问题是如何最好地利用它们来提高性能。

谁来处理TLB的失误?

我们必须回答的一个问题是:谁来处理TLB的缺失?两个答案是可能的:硬件或软件(操作系统)。在过去,硬件有复杂的指令集(有时称为CISC,用于复杂指令集计算机),而构建硬件的人并不信任那些狡猾的操作系统人员。因此,硬件将完全处理TLB缺失。要做到这一点,硬件必须准确地知道页表在内存中的位置(通过一个pagetable基寄存器,在图19.1的第11行中使用),以及它们的确切格式;在一个失误,在一个缺失的情况下,硬件会在页表中运行,找到正确的页表条目并提取所需的翻译,用翻译更新TLB,并重试指令。一个具有硬件管理的TLBs的旧体系结构的例子是Intel x86体系结构,它使用一个固定的多层页面表(详见下一章);当前页表由CR3寄存器指向[I09]。

分页:更快转换--(TLBs)_第4张图片

更现代的架构(例如,MIPS R10k [H93]或Sun s SPARC v9 [wg50], RISC或简化指令集计算机)都有所谓的软件管理的TLB。在TLB缺失时,硬件只是抛出一个异常(图19.3中的第11行),它暂停当前的指令流,将特权级别提升到内核模式,并跳到一个陷阱处理程序。正如您可能猜到的,这个陷阱处理程序是在操作系统中编写的代码,其目的是处理TLB丢失。运行时,代码将查找页表中的转换,使用特殊的特权指令来更新TLB,并从陷阱中返回;此时,硬件重试指令(导致TLB命中)。

让我们来讨论一些重要的细节。首先,返回-trap指令需要与我们之前看到的为系统调用服务时所看到的返回陷阱有一点不同。在后一种情况下,返回-fromtrap应该在陷阱进入操作系统后的指令中恢复执行,就像从过程调用返回到调用过程后立即返回指令一样。在前一种情况下,当从TLB错误处理陷阱返回时,硬件必须在导致陷阱的指令下恢复执行;这样重试使指令再次运行,这一次导致TLB命中。因此,根据陷阱或异常的原因,硬件必须在捕获到操作系统时保存不同的PC,以便在需要时恢复正常。

其次,在运行TLB错误处理代码时,操作系统需要格外小心,以免导致TLB错误的无限链发生。许多解决方案;例如,您可以在物理内存中保留TLB的处理程序(它们未被映射,且不受地址转换的影响),或者保留TLB中的一些条目用于永久有效的翻译,并为处理程序代码本身使用一些永久的翻译槽;这些连线的翻译总是击中TLB。软件管理方法的主要优点是灵活性:操作系统可以使用任何它想要实现页表的数据结构,而不需要硬件更改。另一个优点是简单;正如您在TLB控制流中看到的(图19.3中的第11行,与图19.1中的第11 19行相反),硬件不需要做太多的错误操作;它引发了一个异常,而OS TLB miss handler负责其余部分。

RISC和CISC

在20世纪80年代,计算机建筑界发生了一场伟大的战争。其中一方是CISC阵营,它支持复杂的指令集计算;另一方面是RISC,用于减少指令集计算[PS81]。RISC方面由大卫·帕特森(David Patterson)和斯坦福大学(Stanford)的约翰·亨尼斯(John Hennessy)领衔,他们也是一些著名书籍的合著者,尽管后来约翰·库克因其在RISC [cm50]上的早期作品而获得图灵奖。CISC指令集往往有很多指令,每条指令都是相对强大的。例如,您可能会看到一个字符串副本,它接收两个指针和一个长度,并从源到目的地复制字节。CISC背后的想法是,指令应该是高级的原语,使汇编语言本身更容易使用,并使代码更紧凑。

RISC指令集正好相反。RISC背后的一个关键观察是,指令集实际上是编译器的目标,所有编译器真正想要的是一些简单的原语,它们可以用来生成高性能的代码。因此,RISC的支持者认为,让我们尽可能多地从硬件(特别是微代码)中分离出来,并让s保持简单、统一和快速。

在早期,RISC芯片产生了巨大的影响,因为它们明显更快[BC91];许多论文都写;一些公司成立了(例如,MIPS和Sun)。然而,随着时间的推移,像英特尔这样的CISC制造商将许多RISC技术加入到其处理器的核心中,例如,通过添加早期的管道阶段,将复杂的指令转化为微指令,然后以类似于riscs的方式处理。这些创新,加上每个芯片上越来越多的晶体管,使得CISC能够保持竞争力。最终的结果是这些创新,加上每个芯片上越来越多的晶体管,使得CISC能够保持竞争力。最终的结果是,争论停止了,现在两种类型的处理器都可以运行得很快。

19.4 TLB内容:里面有什么。

让我们更详细地看一下硬件TLB的内容。一个典型的TLB可能有32、64或128个条目,被称为完全关联。基本上,这仅仅意味着任何给定的转换都可以在TLB中的任何位置,并且硬件将在并行中搜索整个TLB以找到所需的翻译。TLB条目可能是这样的。


注意,VPN和PFN都存在于每个条目中,因为一个转换可能会在这些位置中的任何一个位置(在硬件术语中,TLB被称为全关联缓存)。硬件并行地搜索条目,看是否有匹配。

TLB有效位不等于页表有效位。一个常见的错误是将在TLB中发现的有效位与在页表中找到的有效位混淆。在页表中,当一个页表条目被标记为无效时,这意味着该页面没有被进程分配,也不应该被一个正确的工作程序访问。当访问无效页面时,通常的响应是捕捉到OS,操作系统将通过杀死进程来响应。

相比之下,TLB有效位仅指TLB条目在其内部是否有有效的转换。例如,当系统启动时,每个TLB条目的通用初始状态将被设置为无效,因为在那里没有缓存地址转换。一旦启用了虚拟内存,一旦程序开始运行并访问它们的虚拟地址空间,TLB就会慢慢地填充,因此有效的条目很快就会填充TLB。

在执行上下文切换时,TLB有效位也非常有用,我们将在下面进一步讨论。通过将所有TLB条目设置为无效,系统可以确保即将运行的进程不会意外地使用前一个进程的虚拟到物理的转换。

更有趣的是其他的部分。例如,TLB通常有一个有效位,它表示该条目是否具有有效的翻译。同样常见的是保护位,它决定了如何访问页面(如在页表中)。例如,代码页可能被标记为读和执行,而堆页面可能被标记为读和写。可能还有其他一些字段,包括地址空间标识符、脏位等等;更多信息见下文。

19.5 TLB问题:上下文切换。

使用TLBs时,当在进程之间切换时出现了一些新的问题(也就是地址空间)。特别地,TLB包含了对当前正在运行的进程有效的虚拟到物理翻译;这些翻译对其他过程没有意义。因此,当从一个进程切换到另一个进程时,硬件或操作系统(或两者)必须小心,以确保即将运行的进程不会意外地使用先前运行的进程的翻译。

了更好地理解这种情况,让我们来看一个例子。当一个进程(P1)运行时,它假设TLB可以缓存对其有效的翻译,即。它来自于P1的页表。假设,在本例中,P1的第10个虚拟页映射到物理帧100。

在本例中,假设存在另一个进程(P2),操作系统很快就会决定执行上下文切换并运行它。假设P2的第10个虚拟页面映射到物理框架170。如果两个进程的条目都在TLB中,那么TLB的内容将是。

分页:更快转换--(TLBs)_第5张图片

在上面的TLB中,我们显然有一个问题:VPN 10转换为PFN 100 (P1)或PFN 170 (P2),但是硬件不能区分哪个进程是哪个进程的。因此,为了使TLB能够正确和高效地支持跨多个进程的虚拟化,我们需要做更多的工作。因此,一个关键

  • 症结

如何管理上下文切换中的TLB内容?

在进程之间的上下文切换时,TLB中的转换。 对于即将运行的流程来说,最后一个过程没有意义。 为了解决这个问题,硬件或操作系统应该做些什么?

对于这个问题有许多可能的解决方案。一种方法是简单地在上下文切换中刷新TLB,从而在运行下一个进程之前清空TLB。在基于软件的系统中,可以通过显式(和特权)硬件指令来实现这一点;使用硬件管理的TLB时,可以在更改页表基寄存器时启动刷新(注意,操作系统必须更改上下文切换中的PTBR)。在这两种情况下,刷新操作只将所有有效位设置为0,从而清除TLB的内容。通过在每个上下文切换上刷新TLB,我们现在有了一个工作解决方案,因为这个过程不会偶然在TLB中遇到错误的翻译。但是,有一个代价:每次进程运行时,它必须在接触到它的数据和代码页时发生TLB错误。如果操作系统频繁地在进程之间切换,这个成本可能很高。

为了减少这种开销,一些系统增加了硬件支持,以允许跨上下文切换共享TLB。特别是,一些硬件系统在TLB中提供了一个地址空间标识符(ASID)字段。您可以将ASID看作进程标识符(PID),但通常它的比特数更少(例如,ASID的8比特,PID的32位)。

如果我们从上面的示例TLB中添加ASIDs,那么很明显的过程可以很容易地共享TLB:只有ASID字段才能区分其他相同的翻译。这里是一个TLB和添加的ASID字段的描述。

分页:更快转换--(TLBs)_第6张图片

此,使用地址空间标识符,TLB可以同时保留不同进程的翻译,而不会产生任何混淆。当然,硬件还需要知道当前运行的是哪个进程来执行翻译,因此操作系统必须在上下文切换中设置一些特权寄存器,以适应当前进程的ASID。

顺便说一下,您可能还想到了另一个例子,其中TLB的两个条目非常相似。在这个例子中,两个不同的进程有两个条目,两个不同的VPNs指向同一个物理页面。

分页:更快转换--(TLBs)_第7张图片

例如,当两个进程共享一个页面(例如一个代码页)时,可能会出现这种情况。在上面的示例中,流程1与流程2共享物理页101;P1将此页面映射到其地址空间的第10页,而P2将其映射到地址空间的第50页。共享代码页(在二进制文件或共享库中)是有用的,因为它减少了使用中的物理页面数量,从而减少了内存开销。

19.6问题:替代政策

与任何缓存一样,因此也与TLB一样,我们必须考虑的另一个问题是缓存替换。具体来说,当我们在TLB中安装一个新条目时,我们必须替换旧的条目,因此问题是:要替换哪一个。

关键问题:如何设计TLB替换策略。

当我们添加新的TLB条目时,应该替换哪个TLB条目?当然,我们的目标是尽量减少遗漏率(或提高命中率),从而提高性能。

当我们解决将页面交换到磁盘的问题时,我们将详细研究这些策略;这里我们将重点介绍一些典型的策略。一种常见的方法是将最近使用的或LRU条目驱逐出去。LRU试图利用内存引用流中的位置,假设最近没有使用的条目很可能是被驱逐的好候选者。

另一种典型的方法是使用随机策略,它会随机地驱逐TLB映射。这样的政策是有用的,因为它的简单性和避免角的行为的能力;例如,当一个程序循环超过n + 1页,而TLB大小为n时,一个合理的策略(如LRU)的行为是相当不合理的;在这种情况下,LRU会忽略每个访问,而random则要好得多。

分页:更快转换--(TLBs)_第8张图片

19.7一个真正的TLB条目

最后,让我们简单地看一下真正的TLB。这个例子来自MIPS R4000 [H93],一个使用软件管理的TLBs的现代系统;一个稍微简化的MIPS TLB条目可以在图19.4中看到。

MIPS R4000支持一个具有4KB页面的32位地址空间。因此,我们期望在典型的虚拟地址中有一个20位的VPN和12位的偏移量。但是,正如您在TLB中看到的,VPN只有19个字节;事实证明,用户地址只来自地址空间的一半(其余为内核预留),因此只需要19位VPN。VPN可以转换为一个24位的物理帧数(PFN),因此可以支持最多64GB(物理)主内存(2^24 4KB页)的系统.

在MIPS TLB中还有一些其他有趣的部分。我们看到一个全局位(G),它被用于在进程之间进行全局共享的页面。因此,如果设置了全局位,则忽略ASID。我们还看到了8位ASID,操作系统可以用来区分地址空间(如上所述)。一个问题:如果有超过256(28)个进程在运行,操作系统应该怎么做?最后,我们看到了3个Coherence (C)位,它决定了一个页面如何被硬件缓存(超出了这些注释的范围);当页面出现时标记的脏位。当页面被写入到(我们将在稍后看到使用它)时标记的脏位;一个有效的位,它告诉硬件是否有一个有效的翻译存在于条目中。还有一个页面掩码字段(未显示),它支持多个页面大小;稍后我们将会看到为什么使用较大的页面可能有用。最后,64位中的一些是未使用的(图中阴影灰色)。MIPS TLBs通常有32或64个条目,其中大多数在运行时被用户进程使用。然而,有一些是为操作系统保留的。可以通过操作系统设置一个有线寄存器来告诉硬件TLB为OS预留多少个槽;操作系统使用这些保留的映射来获取在关键时刻访问的代码和数据,其中TLB缺失将会带来问题(例如,在TLB丢失处理程序中)。

因为MIPS TLB是软件管理的,所以需要使用指令来更新TLB。MIPS提供了四个这样的指令:TLBP,它探测TLB,看看是否有特定的翻译,TLBR,它读取TLB进入寄存器的内容;TLBWI,它取代了一个特定的TLB条目;TLBWR,取代了一个随机的TLB条目。操作系统使用这些指令来管理TLB的内容。当然,这些指示是有特权的;想象一下,如果用户进程可以修改TLB的内容(提示:几乎任何内容,

示:RAM并不总是RAM (CULLER定律)。

随机存取存储器(RAM)这个术语意味着,你可以像其他内存一样快速访问RAM的任何部分。虽然以这种方式来考虑RAM通常很好,但是由于TLB等硬件/操作系统特性,访问特定的内存页可能代价很高,特别是如果该页面当前没有被TLB映射。因此,记住实现提示总是好的:RAM不总是RAM。有时随机访问您的地址空间,特别是如果访问的页面数超过TLB覆盖率,可能会导致严重的性能损失。因为我们的一个顾问,David Culler,曾经总是指出TLB是许多性能问题的来源,我们将这条法律命名为他的荣誉:Culler s law。



.

你可能感兴趣的:(Operating,Systems)