3. 页表项(PTE)结构详解
页表项(Page Table Entry, PTE)是页表的核心数据单元,它记录了虚拟页号(VPN)到物理页号(PPN)的映射关系以及页面的访问控制属性。在 RISC-V Sv39 分页机制中,每个 PTE 占用 64 位(8 字节),其结构清晰划分为两个主要部分:高 54 位的物理页号(PPN)和低 10 位的标志位(Flags)。
3.1 PTE 整体位布局
下表展示了 RISC-V Sv39 中一个 64 位 PTE 的完整位域划分。位编号从 0(最低位)到 63(最高位)。
| 位域 | 位范围 | 宽度 | 名称 | 描述 |
|---|---|---|---|---|
| Flags | 9:0 | 10 | V, R, W, X, U, G, A, D, RSW | 页表项的控制与状态标志位 |
| PPN[0] | 18:10 | 9 | 物理页号第 0 部分 | 物理地址的 12-20 位 |
| PPN[1] | 27:19 | 9 | 物理页号第 1 部分 | 物理地址的 21-29 位 |
| PPN[2] | 53:28 | 26 | 物理页号第 2 部分 | 物理地址的 30-55 位 |
| 保留 | 63:54 | 10 | — | 保留位,必须为 0 |
3.2 PPN(物理页号):63-10 位
PPN(Physical Page Number)是 PTE 中最重要的数据部分,它指向物理内存中的一个 4KB 页面的起始地址。PPN 被拆分为三个子段:PPN[0]、PPN[1] 和 PPN[2],这种拆分方式与多级页表的索引机制完全对应。
- PPN[2](位 53:28):最高 26 位,对应物理地址的 30-55 位。
- PPN[1](位 27:19):中间 9 位,对应物理地址的 21-29 位。
- PPN[0](位 18:10):最低 9 位,对应物理地址的 12-20 位。
当 MMU 完成地址转换时,会将 PPN 与虚拟地址中的页内偏移(12 位)拼接,形成最终的物理地址:物理地址 = {PPN[2], PPN[1], PPN[0], offset[11:0]}。
3.3 Flags(标志位):8-0 位
PTE 的低 10 位是标志位,它们控制着页面的访问权限、缓存策略和状态信息。这些标志位是操作系统实现虚拟内存管理、进程隔离和页面置换的基础。
| 位 | 名称 | 全称 | 含义与作用 |
|---|---|---|---|
| 0 | V | Valid | 有效位。V=1 表示该 PTE 有效,MMU 可以使用;V=0 表示无效,访问将触发页错误。 |
| 1 | R | Readable | 可读位。R=1 允许读取该页面;R=0 禁止读取。 |
| 2 | W | Writable | 可写位。W=1 允许写入该页面;W=0 禁止写入。 |
| 3 | X | Executable | 可执行位。X=1 允许从该页面取指令执行;X=0 禁止执行。 |
| 4 | U | User | 用户模式位。U=1 表示用户态(U-mode)可以访问;U=0 表示仅超级用户态(S-mode)可访问。 |
| 5 | G | Global | 全局位。G=1 表示该映射是全局的,在 TLB 刷新时不会被 sfence.vma 指令清除(除非指定了 ASID)。 |
| 6 | A | Accessed | 访问位。由硬件自动置 1,表示该页面已被访问过(读或写)。操作系统可用来实现页面置换算法(如 LRU 近似)。 |
| 7 | D | Dirty | 脏位。由硬件自动置 1,表示该页面已被写入过。操作系统在页面换出时需要将脏页面写回磁盘。 |
| 8 | RSW | Reserved for Supervisor | 保留给操作系统使用。xv6 未使用该位,但其他操作系统可用来存储自定义元数据。 |
| 9 | RSW | Reserved for Supervisor | 同上,保留给操作系统软件使用。 |
R=0, W=0, X=0 是非法的)。这种组合被 MMU 解释为指向下一级页表的指针(非叶子节点),而非最终的页面映射。叶子节点的 PTE 必须至少设置 R 或 X 中的一位。
3.4 权限位组合与页表层级
MMU 通过检查 R、W、X 的组合来判断当前 PTE 是叶子节点还是非叶子节点(指向下一级页表)。这是多级页表遍历的核心判断逻辑。
- 叶子节点(Leaf PTE):当
R=1 或 X=1时,该 PTE 是叶子节点,PPN 直接指向物理页面。此时 W 和 U 位决定具体的访问权限。 - 非叶子节点(Non-leaf PTE):当
R=0, W=0, X=0时,该 PTE 是中间节点,PPN 指向下一级页表的物理地址。此时 V 位必须为 1,其他权限位被忽略。
3.5 xv6 中的 PTE 宏定义
在 xv6 源码的 riscv.h 文件中,PTE 的标志位被定义为宏常量,方便内核代码进行位操作。以下是 xv6 中与 PTE 相关的关键定义:
// 页表项标志位定义 (riscv.h)
#define PTE_V (1L << 0) // Valid
#define PTE_R (1L << 1) // Readable
#define PTE_W (1L << 2) // Writable
#define PTE_X (1L << 3) // Executable
#define PTE_U (1L << 4) // User
#define PTE_G (1L << 5) // Global
#define PTE_A (1L << 6) // Accessed
#define PTE_D (1L << 7) // Dirty
// 从 PTE 中提取 PPN 的掩码
#define PTE2PA(pte) (((pte) >> 10) << 12)
// 从物理地址构造 PTE
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
PTE2PA(pte) 先将 PTE 右移 10 位去掉标志位,再左移 12 位得到物理地址(因为 PPN 是物理页号,需要左移 12 位得到页基址)。PA2PTE(pa) 是逆操作,将物理地址右移 12 位得到 PPN,再左移 10 位放入 PTE 的 PPN 字段。
3.6 A/D 位与硬件辅助的页面管理
Accessed(A)和 Dirty(D)位由 RISC-V 硬件自动维护,为操作系统提供了高效的页面使用统计能力,无需软件模拟。
- A 位(Accessed):当 MMU 对某个页面进行读或写访问时,硬件会自动将该页面对应 PTE 的 A 位置 1。操作系统可以周期性地扫描页表,检查 A 位来判断页面是否被使用过,用于实现页面置换算法(如 Clock 算法)。
- D 位(Dirty):当 MMU 对某个页面进行写操作时,硬件会自动将 D 位置 1。操作系统在页面换出时,只需检查 D 位:如果 D=1,则需要将页面内容写回磁盘;如果 D=0,则可以直接丢弃页面(因为磁盘上的副本仍然有效)。
3.7 PTE 在地址转换中的完整角色
结合多级页表遍历过程,PTE 在每个层级的作用可以总结为以下步骤:
- 读取根页表寄存器:从
satpCSR 中获取根页表的物理地址。 - 逐级索引:使用虚拟地址的 VPN[2]、VPN[1]、VPN[0] 分别索引三级页表。
- 检查 V 位:每读取一个 PTE,首先检查 V 位。如果 V=0,触发页错误。
- 判断节点类型:检查 R 和 X 位。如果 R=0 且 X=0,则 PPN 指向下一级页表,继续遍历;否则,当前 PTE 是叶子节点。
- 权限检查:在叶子节点上,检查 U 位(用户态访问需要 U=1)、W 位(写操作需要 W=1)、X 位(执行需要 X=1)。
- 硬件更新 A/D 位:如果访问成功,硬件自动将 A 位置 1;如果是写操作,还将 D 位置 1。
- 生成物理地址:将 PPN 与虚拟地址的页内偏移(12 位)拼接,得到最终的物理地址。
3.8 本章小结
页表项(PTE)是连接虚拟地址和物理地址的桥梁,其结构设计体现了 RISC-V 架构的简洁与高效:
- PPN(63-10 位):44 位的物理页号,支持最大 2^56 字节的物理地址空间,被拆分为三个子段以匹配多级页表的索引结构。
- Flags(9-0 位):10 个标志位,涵盖有效性(V)、读写执行权限(R/W/X)、特权级隔离(U)、全局映射(G)、硬件辅助统计(A/D)以及操作系统保留位(RSW)。
- 节点类型区分:通过 R 和 X 位的组合,MMU 可以区分叶子节点(最终映射)和非叶子节点(指向下一级页表),这是多级页表遍历的核心机制。
- 硬件自动维护:A 和 D 位由硬件自动置位,为操作系统提供了零成本的页面访问统计能力,是实现页面置换和写回策略的基础。