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:读取根页表基址
从satp.PPN左移 12 位得到根页表的物理基地址base_l1。 - 步骤 2:一级页表查找(L1)
计算一级页表项地址:addr_l1 = base_l1 + VPN[2] * 8(每项 8 字节)。
硬件读取该 PTE,检查其V(有效位)和R(可读位)/W(可写位)/X(可执行位)。
如果 PTE 的R、W、X均为 0,则 PTE 指向下一级页表;否则为叶节点(大页映射)。 - 步骤 3:二级页表查找(L2)
从一级 PTE 中提取PPN[2],左移 12 位得到二级页表基址base_l2。
计算二级页表项地址:addr_l2 = base_l2 + VPN[1] * 8。
读取并检查二级 PTE,判断是否为叶节点。 - 步骤 4:三级页表查找(L3)
从二级 PTE 中提取PPN[1],左移 12 位得到三级页表基址base_l3。
计算三级页表项地址:addr_l3 = base_l3 + VPN[0] * 8。
读取三级 PTE,此时该 PTE 必须为叶节点(R、W、X至少有一个为 1)。 - 步骤 5:组装物理地址
从三级 PTE 中提取PPN[0],左移 12 位得到物理页帧基址,然后与虚拟地址的offset相加:
物理地址 = (PPN[0] << 12) | offset。
硬件遍历流程图: 下图展示了从
satp 出发,经过三级页表查找,最终合成物理地址的完整路径。
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
硬件遍历过程:
- L1 查找: 根页表基址
0x100000+254 * 8=0x1007F0。读取 PTE,假设值为0x2000C001(V=1, R=W=X=0, PPN=0x2000C)。 - L2 查找: 二级页表基址
0x2000C000+116 * 8=0x2000C3A0。读取 PTE,假设值为0x3000A003(V=1, R=W=X=0, PPN=0x3000A)。 - L3 查找: 三级页表基址
0x3000A000+37 * 8=0x3000A128。读取 PTE,假设值为0x5000B02F(V=1, R=1, W=1, X=0, PPN=0x5000B)。 - 合成物理地址:
0x5000B000+0xB0C=0x5000BB0C。
思考题:如果三级 PTE 的
U 位为 0,而当前 CPU 处于用户模式(U-mode),硬件 MMU 会如何处理?答案是:触发访问违例页错误,因为用户模式无法访问 Supervisor 级别的页面。
通过以上流程,硬件 MMU 以确定性的方式完成了从虚拟地址到物理地址的翻译。理解这一过程是后续优化 TLB 刷新策略、实现高效内核页表管理的基础。