内存管理(二)

分页

概述

第一种是将空间分割成不同长度的分片,就像虚拟内存管理中的分段。遗憾的是,这个解决方法存在固有的问题。具体来说,将空间切成不同长度的分片以后,空间本身会碎片化(fragmented),随着时间推移,分配内存会变得比较困难。

因此,值得考虑第二种方法:将空间分割成固定长度的分片。在虚拟内存中,我们称这种思想为分页

分页不是将一个进程的地址空间分割成几个不同长度的逻辑段(即代码、堆、段),而是分割成固定大小的单
元,每个单元称为一页。相应地,我们把物理内存看成是定长槽块的阵列,叫作页帧(pageframe)。每个这样的页帧包含一个虚拟内存页。

组成部分

image-20230328095945964

为了记录地址空间的每个虚拟页放在物理内存中的位置,操作系统通常为每个进程保存一个数据结构,称为页表(page table)。页表的主要作用是为地址空间的每个虚拟页面保存地址转换(address translation),从而让我们知道每个页在物理内存中的位置。为了转换(translate)该过程生成的虚拟地址,我们必须首先将它分成两个组件:虚拟页面号(virtual page number,VPN)和页内的偏移量(offset)

有效位(valid bit)通常用于指示特定地址转换是否有效。

我们还可能有保护位(protection bit),表明页是否可以读取、写入或执行。同样,以这些位不允许的方式访问页,会陷入操作系统。还有其他一些重要的部分,但现在我们不会过多讨论。

存在位(present bit)表示该页是在物理存储器还是在磁盘上(即它已被换出,swapped out)。

参考位(reference bit,也被称为访问位,accessed bit)有时用于追踪页是否被访问,也用于确定哪些页很受欢迎,因此应该保留在内存中。这

快速地址转换(TLB)

因为这些映射信息一般存储在物理内存中,所以在转换虚拟地址时,分页逻辑上需要一次额外的内存访问。每次指令获取、显式加载或保存,都要额外读一次内存以得到转换信息,这慢得无法接受。

我们要增加所谓的地址转换旁路缓冲存储器(translation-lookaside buffer),它就是频繁发生的虚拟到物理地址转换的硬件缓存(cache)。因此,更好的名称应该是地址转换缓存(address-translation cache)。

对每次内存访问,硬件先检查TLB,看看其中是否有期望的转换映射,如果有,就完成转换(很快),不用访问页表(其中有全部的转换映射)。

大体流程:

  1. 从虚拟地址中提取页号(VPN)

  2. 然后检查TLB 是否有该VPN 的转换映射。

    • 如果有,我们有了TLB 命中(TLB hit),这意味着TLB 有该页的转换映射。成功!接下来我们就可以从相关的TLB 项中取出页帧号(PFN),与原来虚拟地址中的偏移量组合形成期望的物理地址(PA),并访问内存,假定保护检查没有失败。

    • 如果CPU 没有在TLB 中找到转换映射(TLB 未命中),我们有一些工作要做。在硬件访问页表来寻找转换映射,并用该转换映射更新TLB,假设该虚拟地址有效,而且我们有相关的访问权限。当TLB 更新成功后,系统会重新尝试该指令,这时TLB 中有了这个转换映射,内存引用得到很快处理。

TLB的组成:

image-20230329092645174

  • VPN:虚拟帧号
  • PEN:物理帧号
  • TLB 通常有一个有效(valid)位:用来标识该项是不是有效地转换映射。
  • 一个地址空间标识符(Address Space Identifier,ASID):用于进程切换使用。
  • 通常还有一些保护(protection)位:用来标识该页是否有访问权限。例如,代码页被标识为可读和可执行,而堆的页被标识为可读和可写。
  • 脏位(dirty):表示该页是否被写入新数据。
  • 全局位(Global,G):用来指示这个页是不是所有进程全局共享的。
  • 一致性位(Coherence,C):决定硬件如何缓存该页。

替换策略:

  • 一种常见的策略是替换最近最少使用(least-recently-used,LRU)的项。LRU尝试利用内存引用流中的局部性,假定最近没有用过的项,可能是好的换出候选项。
  • 另一种典型策略就是随机(random)策略,即随机选择一项换出去。

较小的表

我们现在来解决分页引入的第二个问题:页表太大,因此消耗的内存太多。段增加了几个位,标识程序是否能够读写该段,或执行其中的代码。

解决方案:

  1. 更大的页:减小页表大小:使用更大的页。这种方法的主要问题在于,大内存页会导致每页内的浪费,这被称为内部碎片(internalfragmentation)问题(因为浪费在分配单元内部)。
  2. 混合方法:分页和分段:解决上述问题的办法是使用分页和分段混合使用,这样就解决了内部碎片的问题了。但是这种杂合导致外部碎片再次出现。
  3. 多级页表:能够离散的分布在内存中,解决了混合方法的外部碎片问题。
  4. 反向页表:一般被视为使用正常的系统内存的TLB的片外扩展。与真正的页表不同,它不需要容纳所有的映射。其中的项代表系统的每个物理页,而不是有许多页表(系统的每个进程一个)。页表项告诉我们哪个进程正在使用此页,以及该进程的哪个虚拟页映射到此物理页。(个人理解为:只存了当前正在运行的所有进程所使用的页地址)