4. 三级页表硬件遍历流程:硬件MMU如何从satp寄存器出发,逐级查找页表,最终完成地址翻译

在 RISC-V Sv39 分页机制下,硬件内存管理单元(MMU)负责将虚拟地址(VA)转换为物理地址(PA)。这个过程并非一次性完成,而是通过一个三级页表结构,从根节点开始逐级向下查找。本章将详细拆解硬件 MMU 的遍历流程,揭示 satp 寄存器、页目录项(PTE)与物理页帧之间的协作关系。

4.1 硬件遍历的起点:satp 寄存器

当 CPU 处于 Supervisor 模式且 satp 寄存器的 MODE 字段被设置为 Sv39(值为 8)时,MMU 被激活。硬件进行地址翻译的第一步,就是从 satp 寄存器中读取根页表的物理基地址。

satp 字段 位宽 描述
MODE 4 位 (63:60) 分页模式选择:0=Bare(无分页),8=Sv39(三级页表)
ASID 16 位 (59:44) 地址空间标识符,用于 TLB 刷新优化
PPN 44 位 (43:0) 根页表的物理页帧号,左移 12 位得到物理基地址
关键点:硬件 MMU 不会检查 satp.PPN 指向的物理页是否有效。如果该地址未映射或内容错误,MMU 将直接触发页错误异常(Page Fault),由操作系统内核接管处理。

4.2 虚拟地址的分解:三级索引与页内偏移

在 Sv39 模式下,一个 39 位的虚拟地址被硬件分解为四个部分,用于三级页表遍历和最终物理地址计算。

虚拟地址 (39 位) 分解结构:
+-----------+-----------+-----------+-----------+
|  VPN[2]   |  VPN[1]   |  VPN[0]   |   offset  |
|  9 位     |  9 位     |  9 位     |  12 位    |
|  (38:30)  |  (29:21)  |  (20:12)  |  (11:0)   |
+-----------+-----------+-----------+-----------+
  • VPN[2]:一级页表索引,用于在根页表中查找下一级页表的物理地址。
  • VPN[1]:二级页表索引,用于在二级页表中查找三级页表的物理地址。
  • VPN[0]:三级页表索引,用于在三级页表中查找最终物理页帧号。
  • offset:页内偏移,直接作为物理地址的低 12 位。

4.3 三级页表硬件遍历步骤

硬件 MMU 按照以下固定步骤完成地址翻译。每一步都涉及一次物理内存读取(PTE 加载),共需 3 次内存访问(不考虑 TLB 命中)。

  1. 步骤 1:读取根页表基址
    从 satp.PPN 左移 12 位得到根页表的物理基地址 base_l1。
  2. 步骤 2:一级页表查找(L1)
    计算一级页表项地址:addr_l1 = base_l1 + VPN[2] * 8(每项 8 字节)。
    硬件读取该 PTE,检查其 V(有效位)和 R(可读位)/W(可写位)/X(可执行位)。
    如果 PTE 的 R、W、X 均为 0,则 PTE 指向下一级页表;否则为叶节点(大页映射)。
  3. 步骤 3:二级页表查找(L2)
    从一级 PTE 中提取 PPN[2],左移 12 位得到二级页表基址 base_l2。
    计算二级页表项地址:addr_l2 = base_l2 + VPN[1] * 8。
    读取并检查二级 PTE,判断是否为叶节点。
  4. 步骤 4:三级页表查找(L3)
    从二级 PTE 中提取 PPN[1],左移 12 位得到三级页表基址 base_l3。
    计算三级页表项地址:addr_l3 = base_l3 + VPN[0] * 8。
    读取三级 PTE,此时该 PTE 必须为叶节点(R、W、X 至少有一个为 1)。
  5. 步骤 5:组装物理地址
    从三级 PTE 中提取 PPN[0],左移 12 位得到物理页帧基址,然后与虚拟地址的 offset 相加:
    物理地址 = (PPN[0] << 12) | offset。
硬件遍历流程图: 下图展示了从 satp 出发,经过三级页表查找,最终合成物理地址的完整路径。
三级页表硬件遍历流程(Sv39) satp 寄存器 PPN → 根页表基址 一级页表 (L1) 索引: VPN[2] (9位) PTE → PPN[2] 二级页表 (L2) 索引: VPN[1] (9位) PTE → PPN[1] 三级页表 (L3) 索引: VPN[0] (9位) PTE → PPN[0] 物理地址 虚拟地址分解 (39位) VPN[2] (9位) | VPN[1] (9位) | VPN[0] (9位) | offset (12位) 基址 PPN[2] PPN[1] PPN[0] VPN[2] 索引 L1 页表项 VPN[1] 索引 L2 页表项 VPN[0] 索引 L3 页表项 offset 直接拼接为物理地址低12位

4.4 PTE 结构解析与权限检查

每个页表项(PTE)是一个 64 位的结构,硬件在遍历过程中会检查其权限位。如果权限不满足访问类型(读、写、执行),MMU 会触发访问违例异常。

位域 位宽 标志 描述
63:54 10 RSW 保留给操作系统使用(如脏页、访问位模拟)
53:28 26 PPN[2] 物理页帧号高位(44位PPN的高26位)
27:19 9 PPN[1] 物理页帧号中位
18:10 9 PPN[0] 物理页帧号低位
9:8 2 RSW 保留给操作系统
7 1 D 脏位(Dirty):页面被写入后由硬件置位
6 1 A 访问位(Accessed):页面被读/写后由硬件置位
5 1 G 全局映射(Global):TLB 刷新时跳过
4 1 U 用户模式可访问(User)
3 1 X 可执行(Execute)
2 1 W 可写(Write)
1 1 R 可读(Read)
0 1 V 有效位(Valid):PTE 是否可用
重要规则:如果某一级 PTE 的 V 位为 0,硬件 MMU 立即停止遍历并触发页错误。如果 R、W、X 全为 0,则该 PTE 指向下一级页表(非叶节点);否则为叶节点,包含最终物理页帧号。Sv39 支持 2 MiB 和 1 GiB 的大页映射,分别对应二级 PTE 和一级 PTE 直接作为叶节点。

4.5 硬件遍历的异常处理与性能考量

硬件 MMU 的遍历过程是纯粹的组合逻辑或微代码实现,每次翻译最多需要 3 次物理内存读取。在 xv6 中,由于内核与用户空间使用不同的页表,上下文切换时 satp 被重写,导致 TLB 完全失效,后续翻译必须重新遍历内存中的页表。

场景 硬件行为 软件处理
PTE 有效且权限匹配 正常翻译,返回物理地址 无操作
PTE.V == 0 触发 Page Fault(异常代码 12) 内核分配物理页并建立映射
权限不足(U/S 位或 R/W/X 不匹配) 触发 Page Fault(异常代码 12/15) 内核检查是否可提升权限或终止进程
PTE.A == 0 或 PTE.D == 0 硬件置位 A/D 位(若 PTE 可写) 内核利用 A/D 位实现页面置换算法
大页映射(L1/L2 叶节点) 跳过后续级别,直接合成物理地址 内核需保证大页内所有地址有效
xv6 中的简化:xv6 内核不使用大页映射,所有映射均为 4 KiB 标准页。因此硬件遍历总是走完三级页表,不会在 L1 或 L2 提前终止。此外,xv6 不依赖硬件 A/D 位,而是通过软件模拟(每次访问都视为脏页),简化了页表管理逻辑。

4.6 地址翻译的完整示例

假设 satp.PPN = 0x100(根页表物理地址 0x100000),虚拟地址 0x7F3A4B0C。分解该虚拟地址:

虚拟地址: 0x7F3A4B0C
二进制:   0111 1111 0011 1010 0100 1011 0000 1100
分解:
  VPN[2] = (38:30) = 0xFE (254)
  VPN[1] = (29:21) = 0x74 (116)
  VPN[0] = (20:12) = 0x25 (37)
  offset = (11:0)  = 0xB0C

硬件遍历过程:

  1. L1 查找: 根页表基址 0x100000 + 254 * 8 = 0x1007F0。读取 PTE,假设值为 0x2000C001(V=1, R=W=X=0, PPN=0x2000C)。
  2. L2 查找: 二级页表基址 0x2000C000 + 116 * 8 = 0x2000C3A0。读取 PTE,假设值为 0x3000A003(V=1, R=W=X=0, PPN=0x3000A)。
  3. L3 查找: 三级页表基址 0x3000A000 + 37 * 8 = 0x3000A128。读取 PTE,假设值为 0x5000B02F(V=1, R=1, W=1, X=0, PPN=0x5000B)。
  4. 合成物理地址: 0x5000B000 + 0xB0C = 0x5000BB0C。
思考题:如果三级 PTE 的 U 位为 0,而当前 CPU 处于用户模式(U-mode),硬件 MMU 会如何处理?答案是:触发访问违例页错误,因为用户模式无法访问 Supervisor 级别的页面。

通过以上流程,硬件 MMU 以确定性的方式完成了从虚拟地址到物理地址的翻译。理解这一过程是后续优化 TLB 刷新策略、实现高效内核页表管理的基础。