OS-分页与页表

快速地址转换(TLB)

在《OS-内存管理》中指出,分页机制的一个主要缺点是可能导致额外的内存访问开销。为了加速虚拟地址到物理地址的映射过程,避免频繁查询页表,计算机系统采用了常见的加速技术——缓存。

TLB:TLB 是一种专门用于缓存虚拟地址到物理地址转换结果的硬件缓存。在进行内存访问时,硬件首先检查 TLB,而不是直接查询页表,从而显著减少地址转换的开销。

TLB 未命中处理:当发生 TLB 未命中时,不同架构的设计会决定由硬件还是操作系统来处理。例如,x86 架构由硬件自动处理 TLB 未命中,而一些精简指令集(如 MIPS)则会触发异常,由操作系统接管 TLB 未命中的处理。

注意,操作系统的设计者必须确保处理 TLB 未命中的程序本身不会触发 TLB 未命中,否则可能导致无限递归。常见的解决方法有两种:

  • 直接不映射 TLB 处理程序,这样使用 TLB 处理程序根本无需进行虚拟地址到物理地址的转换。
  • 让 TLB 永久记住 TLB 处理程序相关的地址,保证 TLB 处理程序本身不会触发 TLB 未命中。

当假设在 x86 下采用简单线性页表时,TLB 的工作流程如下:

graph TD
    A[CPU 发出虚拟地址] --> B{分解虚拟地址}
    B --> |提取 VPN 和偏移量| C[查询 TLB]
    C --> D{TLB 命中?}
    D --> |是| E[检查保护位]
    E --> F{权限有效?}
    F --> |是| G[组合 PFN 和偏移量]
    G --> H[访问物理内存]
    D --> |否| I[触发 TLB Miss 异常]
    I --> J[操作系统处理]
    J --> K[遍历线性页表]
    K --> L{页表项有效?}
    L --> |是| M[更新 TLB]
    M --> N[返回用户模式]
    N --> C
    L --> |否| O[触发页错误]

TLB 中也存在“有效位”。但注意:TLB 的有效位和页表中的有效位不是同一个概念。

页表中的有效位:页表中的有效位用于标记虚拟地址是否已被分配对应的物理帧。如果页表项被标记为无效,则表示该虚拟地址尚未映射到物理内存,程序访问该地址时会触发异常。

TLB 中的有效位:TLB 中的有效位用于标记 TLB 条目本身是否有效。例如,系统启动时,TLB 中的所有条目通常被初始化为无效状态,因为尚未缓存任何地址转换映射。随着程序的运行,TLB 逐渐被填充,有效位被设置为有效。此外,TLB 中的有效位在进程切换时尤为重要,它可以确保新进程不会错误地使用上一个进程残留的虚拟地址到物理地址的映射。

上下文切换与 TLB

虚拟内存为每个进程提供了独占整个内存空间的假象,因此可能出现多个进程在相同的虚拟地址上分配内存,但各自映射到不同物理地址的情况。这时,TLB 需要能够处理不同进程相同虚拟地址分别映射到不同物理地址的需求

最简单的方法是,在进行上下文切换时将 TLB 所有有效位均设为 0,这样新的进程运行时必然触发 TLB 未命中进而更新 TLB 映射。但这种方法的缺点是每次上下文切换都会导致 TLB 被清空,新进程需要重新填充 TLB,增加了 TLB 未命中的开销。特别是在频繁上下文切换的场景下,TLB 的加速效果会被显著削弱

另一种方式是让 TLB 能够区分不同的进程,并为不同进程返回不同映射。ASID 类似于进程标识符(PID),但位数更少(例如 8 位),用于区分不同进程的地址映射。通过为每个 TLB 条目附加 ASID,TLB 可以同时缓存多个进程的地址映射,而不会产生冲突。在上下文切换时,操作系统只需更新当前进程的 ASID,而无需清空 TLB,从而避免了频繁的 TLB 未命中。

更小的页表

一个现代系统可能有上百个进程,比如我当前的 win10 就有 265 个进程,即便每个页表只占用 4 MB 也有近 1GB 的空间被用于存储页表——太浪费了!

必须让页表变小!

更大的页:最简单的办法是选择增大单个页的大小,从而用更少的页分配更多的内存,进而减小页表的大小。然而,这种方法的主要问题是会导致每页内未使用的内存增加,即内部碎片问题。

混合方法:分页和分段
另一种方法是结合分页和分段技术来减少页表的内存开销。通过将地址空间分成不同的段,并为每个段单独分配页表,可以减少页表中的无效项数量。例如,假设一个地址空间中堆和栈的使用部分很小,可以通过分段来减少页表中的无效项。但是,这种杂合方法仍然存在一些问题,比如外部碎片和对特定地址空间使用模式的依赖。

多级页表

多级页表通过树状层级结构有效减少了线性页表的内存占用。它将单一大页表拆分为多个层级(通常为2-4级),并仅在内存区域实际使用时才分配对应的页表空间。

在采用多级页表时,虚拟地址被划分为以下部分:

1
| 页目录索引 | 页表索引 | 页内偏移 |

地址转换过程如下:

  1. 使用页目录索引查询页表目录,获取具体的页表。
  2. 使用页表索引查询页表,获取实际物理地址。

页表目录中引入了有效位,用于指示当前页表目录页内是否存在有效的页表。若无效,则不分配该页表目录所需的内存。这种设计有以下优点:

  • 减少为无效页表分配的内存,支持稀疏地址空间。
  • 页表可以离散存放,无需像线性页表那样占用连续的内存空间。

然而,多级页表也存在缺点:当 TLB 未命中时,需要进行更多的内存访问才能完成地址转换。但在 TLB 命中率较高的情况下,这一缺点的影响可以忽略。

updatedupdated2025-04-072025-04-07