4. x86架构页表层级:32位PAE模式下的三级结构
在32位PAE(Physical Address Extension)模式下,x86架构将传统的二级页表扩展为三级页表结构,以支持最大64GB的物理内存寻址。PAE模式通过引入页目录指针表(PDPT)作为顶层,配合页目录(PD)和页表(PT),实现了对36位物理地址的映射。本节将深入剖析这三级的组成、索引方式及权限控制机制。
核心要点:PAE模式下的三级页表结构将线性地址拆分为4个字段:PDPT索引(2位)、PD索引(9位)、PT索引(9位)以及页内偏移(12位)。每个页表项(PTE)从32位扩展为64位,以容纳36位物理地址和扩展的权限标志。
4.1 三级页表结构总览
下图展示了32位PAE模式下线性地址到物理地址的完整转换路径。CPU通过CR3寄存器定位PDPT基址,经过三级查表最终获得物理页框号(PFN)。
4.2 三级页表的索引与寻址机制
32位线性地址在PAE模式下被分解为4个字段,每个字段对应一级页表的索引或页内偏移。下表详细列出了各字段的位宽、作用及寻址范围。
| 字段名称 | 位范围 | 位宽 | 作用 | 寻址条目数 |
|---|---|---|---|---|
| PDPT索引 | [31:30] | 2位 | 选择页目录指针表中的条目 | 4个PDPTE |
| PD索引 | [29:21] | 9位 | 选择页目录中的条目 | 512个PDE |
| PT索引 | [20:12] | 9位 | 选择页表中的条目 | 512个PTE |
| 页内偏移 | [11:0] | 12位 | 定位物理页内的字节 | 4096字节 |
寻址能力计算:4(PDPT条目)× 512(PD条目)× 512(PT条目)× 4KB(页大小)= 4GB线性地址空间。PAE模式并未扩大线性地址空间(仍为32位),但通过64位PTE扩展了物理地址宽度。
4.3 页目录指针表(PDPT)结构
PDPT是PAE模式引入的顶层页表结构,位于物理内存中,其基址由CR3寄存器的高27位指定(32位模式下CR3的低5位被忽略,因为PDPT必须32字节对齐)。PDPT包含4个64位的条目(PDPTE),每个条目指向一个页目录(PD)。
4.4 页目录(PD)与页表(PT)结构
页目录(PD)和页表(PT)在PAE模式下均包含512个64位条目。PD中的每个条目(PDE)指向一个页表(PT),而PT中的每个条目(PTE)指向一个4KB物理页框。两者的条目格式高度相似,但PDE支持2MB大页映射(当PS位=1时)。
| 条目类型 | 条目数 | 条目大小 | 映射粒度 | 特殊标志位 |
|---|---|---|---|---|
| PDE(页目录条目) | 512 | 8字节 | 4KB(正常)或 2MB(大页) | PS位(位7):0=4KB页,1=2MB大页 |
| PTE(页表条目) | 512 | 8字节 | 4KB | PAT位(位7):页属性表索引 |
大页映射注意事项:当PDE的PS位为1时,PDE直接映射2MB物理内存,此时不再查询PT。PDE的[31:21]位提供2MB页框的高位地址,线性地址的[20:0]作为页内偏移。大页映射可减少TLB压力,但要求物理内存连续2MB对齐。
4.5 64位PTE的权限控制字段
PAE模式下,每个PTE扩展为64位,新增了多个权限控制位。下表列出了关键权限字段及其作用。
| 位域 | 名称 | 作用描述 |
|---|---|---|
| 位0 | P(Present) | 存在位:1=页在物理内存中,0=页不在(触发缺页异常) |
| 位1 | R/W(Read/Write) | 读写权限:0=只读,1=可读写 |
| 位2 | U/S(User/Supervisor) | 特权级:0=仅内核态(CPL<3)可访问,1=用户态(CPL=3)也可访问 |
| 位3 | PWT(Page Write-Through) | 页级写透缓存策略 |
| 位4 | PCD(Page Cache Disable) | 页级缓存禁用 |
| 位5 | A(Accessed) | 访问位:由CPU硬件置1,表示页被访问过 |
| 位6 | D(Dirty) | 脏位:由CPU硬件置1,表示页被写入过(仅PTE有效) |
| 位7 | PS/PAT | PDE中为大页标志;PTE中为PAT索引 |
| 位8 | G(Global) | 全局页:置1时TLB刷新时不被清除(需CR4.PGE=1) |
| 位[11:9] | AVL(Available) | 软件可用位:操作系统可自定义用途 |
| 位[35:12] | PFN(Page Frame Number) | 物理页框号:对应36位物理地址的高24位 |
| 位[63:36] | Reserved | 保留位:必须为0,否则引发#GP异常 |
4.6 地址转换完整流程
以下步骤描述了CPU在PAE模式下如何将线性地址转换为物理地址:
- 读取CR3:CPU从CR3寄存器获取PDPT的物理基址(32字节对齐)。
- 查询PDPT:使用线性地址[31:30]作为索引,从PDPT中读取对应的PDPTE。检查PDPTE的P位,若为0则触发缺页异常。
- 查询PD:从PDPTE中提取PD基址(位[35:12]),使用线性地址[29:21]作为索引读取PDE。检查PDE的P位和PS位:
- 若PS=0:继续步骤4。
- 若PS=1:PDE直接映射2MB大页,物理地址 = (PDE[31:21] << 21) | 线性地址[20:0]。
- 查询PT:从PDE中提取PT基址(位[35:12]),使用线性地址[20:12]作为索引读取PTE。检查PTE的P位。
- 组合物理地址:物理地址 = (PTE[35:12] << 12) | 线性地址[11:0]。
- 权限检查:CPU根据PTE的R/W、U/S位以及当前CPL进行访问权限校验,不匹配则触发#GP异常。
4.7 权限控制与保护机制
PAE模式下的权限检查在每一级页表查询时都会执行,但最终的有效权限由PDE和PTE的权限字段取交集决定。以下是权限组合规则:
- U/S位:若PDE或PTE中任一U/S=0,则该页为超级用户页(仅内核可访问)。只有两级U/S均为1时,用户态才可访问。
- R/W位:若PDE或PTE中任一R/W=0,则该页为只读页。只有两级R/W均为1时,页才可写。
- NX位(执行禁用):在支持XD/NX的CPU上,PTE的位63作为执行禁用位。置1时该页不可执行代码,用于防止缓冲区溢出攻击。
实践建议:在配置PAE页表时,建议在PDE级别设置较宽松的权限(如U/S=1, R/W=1),而在PTE级别进行精细控制。这样可以在不频繁修改PD的情况下,灵活调整单个页面的权限。例如,代码段可在PDE设R/W=0,PTE设U/S=1;数据段在PDE设R/W=1,PTE根据需求设置。
4.8 典型配置示例
以下代码片段展示了如何在C语言中定义PAE页表项结构体,并初始化一个简单的页表映射:
/* PAE模式页表项结构定义(64位) */
typedef struct {
uint64_t present : 1; /* 位0: 存在位 */
uint64_t rw : 1; /* 位1: 读写 */
uint64_t us : 1; /* 位2: 用户/超级用户 */
uint64_t pwt : 1; /* 位3: 写透 */
uint64_t pcd : 1; /* 位4: 缓存禁用 */
uint64_t accessed : 1; /* 位5: 访问位 */
uint64_t dirty : 1; /* 位6: 脏位(仅PTE) */
uint64_t ps_or_pat : 1; /* 位7: PDE大页/PTE PAT */
uint64_t global : 1; /* 位8: 全局页 */
uint64_t avail : 3; /* 位[11:9]: 软件可用 */
uint64_t pfn : 24; /* 位[35:12]: 物理页框号 */
uint64_t reserved : 28; /* 位[63:36]: 保留 */
} __attribute__((packed)) pae_pte_t;
/* 初始化一个用户态可读写页表条目 */
void init_user_rw_pte(pae_pte_t *pte, uint64_t phys_addr) {
pte->present = 1;
pte->rw = 1; /* 可读写 */
pte->us = 1; /* 用户态可访问 */
pte->pwt = 0;
pte->pcd = 0;
pte->accessed= 0;
pte->dirty = 0;
pte->ps_or_pat=0;
pte->global = 0;
pte->avail = 0;
pte->pfn = (phys_addr >> 12) & 0xFFFFFF;
pte->reserved= 0;
}
调试技巧:在QEMU中启用PAE模式时,可使用
info tlb 命令查看当前TLB缓存,或使用 info mem 查看页表结构。若遇到#GP异常,检查保留位(位[63:36])是否被错误置1,这是PAE模式下最常见的配置错误。