4. 页表项标志位:Present/RW/US/Accessed/Dirty/Page Size/Global/NX 等标志位的含义与用途

x86_64 架构下的页表项(Page Table Entry, PTE)不仅仅是一个物理地址指针,其低 12 位承载了丰富的控制与状态标志位。这些标志位由硬件 MMU 在地址翻译过程中直接解析,用于控制页面的访问权限、缓存策略、全局性以及记录页面的使用状态。理解这些标志位是掌握分页机制和操作系统内存管理的关键。

4.1 页表项格式总览

在 4KB 页面的标准分页模式下,一个 64 位的页表项结构如下。高 52 位(或 48 位,取决于物理地址扩展)用于存放物理页帧号(PFN),低 12 位为标志位。

关键概念: 标志位由硬件在 TLB 未命中时自动检查,操作系统通过修改这些标志位来控制进程的内存访问行为,而无需修改页表项的物理地址部分。
x86_64 页表项(PTE)位布局(4KB 页面) 物理页帧号(PFN)[位 12:63] P R/W U/S PWT PCD A D PS G RSVD NX 0 1 2 3 4 5 6 7 8 9-11 63 权限/状态 缓存/保留 访问位

4.2 核心权限与存在标志位

以下三个标志位构成了最基本的页面访问控制模型,MMU 在每次内存访问时都会检查它们。

标志位 位索引 含义 用途与行为
P (Present) 位 0 页面是否存在 若为 0,MMU 触发 Page Fault (#PF)。操作系统利用此位实现请求调页和页面交换。当页面被换出到磁盘时,P 位清零,PTE 中的剩余位可用于存储磁盘交换地址。
R/W (Read/Write) 位 1 读写权限 0 表示只读,1 表示可读写。当 CPL < 3(内核态)时,此位通常被忽略(除非 CR0.WP 置位)。CR0.WP=1 时,内核也无法写入只读页,这是实现写时复制 (COW) 和内核只读数据保护的基础。
U/S (User/Supervisor) 位 2 特权级 0 表示超级权限(内核态),1 表示用户权限。CPL=3 的用户态代码只能访问 U/S=1 的页面。此位实现了内核与用户空间的隔离保护。
讲师提示: 当 P 位为 0 时,MMU 不会检查其他任何标志位,直接触发缺页异常。因此,操作系统在实现“页表项交换”时,可以安全地将磁盘块号等元数据覆盖存储在 PTE 的高位中。

4.3 状态跟踪标志位:A 与 D

这两个位由硬件 MMU 自动设置,为操作系统提供页面使用情况的反馈,是实现页面置换算法(如 LRU 近似算法)和文件系统同步的关键。

  • A (Accessed) — 位 5: 只要页面被读取或写入,MMU 就会将此位置 1。操作系统定期扫描页表并清除此位,用于统计页面访问频率。
  • D (Dirty) — 位 6: 仅当页面被写入时,MMU 将此位置 1。表示该页面自被加载到内存后已被修改,在换出时必须写回磁盘(或备份存储)。
注意: A 和 D 位由硬件设置,但通常需要由操作系统软件清零。在 SMP(对称多处理)系统中,对 A/D 位的操作需要考虑缓存一致性,Linux 内核使用特殊的原子指令或 TLB 失效流程来安全地操作它们。
Accessed (A) 与 Dirty (D) 位状态转换 初始状态 A=0, D=0 读取后 A=1, D=0 写入后 A=1, D=1 读 写 硬件自动置位,操作系统负责定期清零 A 位;D 位一旦置 1,仅能通过软件清零。 D=1 的页面在换出时必须写回磁盘。

4.4 页面大小与全局标志位

这两个标志位控制着分页的粒度和 TLB 的缓存行为,对性能有直接影响。

标志位 位索引 含义 用途与行为
PS (Page Size) 位 7 页面大小 仅在 PML4 和 PDPT 表中有效。0 表示下一级是标准页表(4KB 页面),1 表示此条目映射一个大页面(2MB 或 1GB)。Linux 内核利用此位实现透明大页 (THP),减少 TLB 压力。
G (Global) 位 8 全局页面 当 CR4.PGE 置位时,G=1 的页面在 TLB 中不会被普通 INVLPG 指令或 CR3 切换刷新。常用于映射内核空间的固定虚拟地址(如 vmalloc 区域),避免进程切换时频繁刷新 TLB。
性能要点: 全局页面(G=1)是减少上下文切换开销的重要手段。当 CR3 切换发生时(例如从进程 A 切换到进程 B),TLB 中所有非全局条目都会被自动刷新,而全局条目保留。这保证了内核地址空间的映射始终有效。

4.5 NX 与缓存控制标志位

这些标志位提供了更细粒度的控制和安全性增强。

  • NX (No Execute) — 位 63: 在 x86_64 中引入,是硬件级 DEP(数据执行保护)的核心。当 NX=1 时,该页面不允许执行代码。操作系统将栈、堆和数据段标记为 NX,防止缓冲区溢出攻击执行恶意 shellcode。
  • PWT (Page Write-Through) — 位 3: 控制 Cache 的写策略。1 表示 Write-Through(写直达),0 表示 Write-Back(写回)。通常用于设备内存或帧缓冲,以保证写操作的顺序性。
  • PCD (Page Cache Disable) — 位 4: 禁止该页面的 Cache。1 表示该页不可被 CPU 缓存,用于映射MMIO 内存区域或设备寄存器,避免 CPU 缓存导致的数据不一致。
NX 位:阻止代码在数据段执行 代码段 (.text) NX=0, 可执行 R+X 数据段 (.data/.bss) NX=1, 不可执行 R+W 栈 (.stack) NX=1, 不可执行 R+W 溢出跳转 MMU 检查 NX=1,触发 #PF

4.6 标志位组合与典型应用场景

操作系统通过组合不同的标志位来实现复杂的内存管理策略。下表总结了 Linux 内核中常见的标志位组合。

场景 P R/W U/S NX G 说明
用户态代码页 1 0 1 0 0 用户可读可执行,不可写
用户态数据页 1 1 1 1 0 用户可读写,不可执行(DEP)
内核代码页 1 0 0 0 1 内核只读可执行,全局 TLB 缓存
内核数据页 1 1 0 1 1 内核读写,不可执行,全局 TLB 缓存
缺页/换出 0 — — — — P=0 触发缺页异常,高位存磁盘地址
写时复制 (COW) 1 0 1 1 0 用户只读,写时触发 #PF,内核复制页面
讲师提示: 注意“写时复制”场景中,R/W 位被强制设为 0。当用户进程尝试写入时,MMU 检测到只读权限且 CR0.WP=1,触发写保护缺页异常。Linux 内核的缺页异常处理程序会判断这是 COW 场景,分配新物理页并复制内容,然后将 PTE 的 R/W 位置 1 后重新执行指令。

4.7 标志位检查流程

MMU 在地址翻译过程中,会按以下顺序检查页表项的标志位。任何一项检查失败都会立即触发缺页异常(#PF),并记录失败原因到 CR2 寄存器(线性地址)和错误码中。

  1. 存在性检查: 检查 P 位。若为 0,直接触发 #PF(错误码位 0 置 1)。
  2. 权限检查: 检查 U/S 位。若 CPL=3 且 U/S=0,触发 #PF(错误码位 2 置 1)。
  3. 读写检查: 检查 R/W 位。若为写操作且 R/W=0,且 CR0.WP=1,触发 #PF(错误码位 1 置 1)。
  4. 执行检查: 若为取指操作,检查 NX 位。若 NX=1,触发 #PF(错误码位 4 置 1)。
  5. 状态更新: 所有检查通过后,MMU 硬件自动将 A 位置 1;若为写操作,同时将 D 位置 1。
硬件自动操作: A 和 D 位的置位是硬件自动完成的,不需要软件干预。但需要注意的是,在多核系统中,对同一页表的并发访问可能导致 A/D 位的更新出现竞争,Linux 内核通过页表锁(page table lock)来保护这些操作。