4. TLB 命中与缺失:TLB hit/miss 流程、硬件遍历页表与软件填充
TLB 作为页表的硬件缓存,其命中与缺失处理直接决定了虚拟地址翻译的延迟。本章深入剖析 TLB 命中的快速路径、缺失时的两种处理模式(硬件遍历 vs. 软件填充),以及多核环境下 TLB 缺失处理的同步考量。
4.1 TLB 命中流程:快速地址翻译路径
当 CPU 发出虚拟地址 VA 请求时,MMU 并行查询 TLB。若 TLB 命中,则在一个时钟周期内完成翻译,不产生任何流水线停顿。
TLB 命中关键路径(以 4KB 页、两级页表为例):
- 从虚拟地址
VA[31:12]提取 VPN(虚拟页号)。 - TLB 控制器对 VPN 进行索引(Index)和比较(Tag Compare)。
- 若 Tag 匹配且有效位(V)为 1,则命中。
- 从 TLB 项中读出物理页号(PPN)和权限位(R/W/X)。
- 组合 PPN 与页内偏移
VA[11:0]生成物理地址PA。 - 同时检查权限:若违反(如写只读页),触发缺页异常或保护错误。
4.2 TLB 缺失流程:硬件遍历页表
当 TLB 未命中时,MMU 必须从内存中的多级页表获取页表项(PTE)。硬件遍历模式由 MMU 状态机自动完成,无需软件干预。
| 步骤 | 操作 | 访问资源 | 延迟(周期) |
|---|---|---|---|
| 1 | 从 CR3 寄存器读取页表基址 | CPU 内部寄存器 | 0 |
| 2 | VPN[31:22] 索引 L1 页表(PML4) | L1 页表(内存) | ~100 |
| 3 | VPN[21:12] 索引 L2 页表(Page Directory) | L2 页表(内存) | ~100 |
| 4 | 读取 PTE,检查 Present 位 | L2 页表项 | 0(合并) |
| 5 | 将 PTE 写入 TLB,设置有效位 | TLB 硬件 | ~10 |
| 6 | 重试原指令,TLB 命中 | — | 0(重放) |
硬件遍历优势: 无需软件异常处理,对操作系统透明;支持页表 walker 缓存(PWC)进一步加速。x86-64 架构采用此模式。
4.3 软件填充 TLB:操作系统异常处理
在 RISC 架构(如 MIPS、早期 Alpha)中,TLB 缺失由软件异常处理程序完成。MMU 触发 TLB 缺失异常,操作系统内核接管并手动遍历页表、填充 TLB。
软件填充的代价: 异常处理涉及上下文切换、异常向量查找、内核栈切换,通常需要数百个周期。但软件填充提供了最大的灵活性,允许操作系统实现非标准页表结构或自定义替换策略。
// MIPS TLB 缺失异常处理程序(简化)
tlb_miss_handler:
// 从 BadVAddr 寄存器读取缺失虚拟地址
mfc0 $k0, $8 // $k0 = BadVAddr
// 提取 VPN
srl $k0, $k0, 12 // 右移 12 位得到 VPN
sll $k0, $k0, 2 // 乘以 4(PTE 大小)
// 从页表基址寄存器读取
mfc0 $k1, $2 // $k1 = PageTableBase
// 计算 PTE 地址
addu $k1, $k1, $k0 // $k1 = PTE 地址
// 加载 PTE
lw $k0, 0($k1) // $k0 = PTE 值
// 检查 Present 位
andi $k1, $k0, 0x1 // 检查最低位
beq $k1, $zero, page_fault
// 写入 TLB
mtc0 $k0, $2 // 写入 TLB EntryLo
tlbwr // 随机替换 TLB 项
// 返回
eret
4.4 硬件遍历 vs. 软件填充:对比分析
| 特性 | 硬件遍历(Hardware Walk) | 软件填充(Software Fill) |
|---|---|---|
| 典型架构 | x86-64, ARMv8-A | MIPS, SPARC, 早期 Alpha |
| 缺失延迟 | ~400 周期(四级页表) | ~800-1500 周期(含异常开销) |
| 页表结构灵活性 | 低(硬件固定页表格式) | 高(软件可自定义) |
| TLB 替换策略 | 硬件 LRU/Random | 软件可编程(如 FIFO, Clock) |
| 多核同步复杂度 | 低(硬件保证原子性) | 高(需软件处理 TLB 一致性) |
| 功耗 | 较高(硬件状态机持续运行) | 较低(异常处理时 CPU 全速运行) |
4.5 多核环境下的 TLB 缺失处理
在多核系统中,TLB 缺失处理必须考虑页表一致性:一个核可能正在修改页表,而另一个核正在遍历同一页表。硬件遍历通常通过以下机制保证正确性:
- 页表锁(Page Table Lock): 修改页表前获取自旋锁,确保遍历时页表不被修改。
- 原子 PTE 更新: 使用 CAS(Compare-And-Swap)或 LL/SC 指令原子更新 PTE 的 Present 位和权限位。
- TLB 射程无效(TLB Shootdown): 当核 A 修改页表后,必须通过 IPI 通知其他核刷新相关 TLB 项(详见后续章节)。
关键设计原则: 硬件遍历页表时,MMU 必须保证读取的 PTE 是原子一致的。x86-64 通过硬件保证单次 PTE 读取的原子性(8 字节对齐),但多级页表遍历的整体一致性仍需软件通过内存屏障(MFENCE)和页表锁来维护。
4.6 实战案例:x86-64 TLB 缺失性能分析
以 Intel Skylake 微架构为例,TLB 缺失的典型延迟如下:
| TLB 层级 | 容量 | 命中延迟 | 缺失处理 | 缺失延迟 |
|---|---|---|---|---|
| L1 DTLB | 64 项 | 1 周期 | L2 TLB 命中 | ~8 周期 |
| L2 TLB | 1536 项 | ~8 周期 | 硬件页表遍历 | ~400 周期 |
| 页表遍历 | — | — | PWC 命中 | ~100 周期 |
| 页表遍历 | — | — | PWC 缺失 | ~400 周期 |
优化建议: 使用大页(2MB/1GB)可显著降低 TLB 缺失率。2MB 大页覆盖 512 倍于 4KB 页的地址空间,L2 TLB 可覆盖 3GB 内存,大幅减少页表遍历次数。