2. 页表结构详解:多级页表(L1/L2/L3)、页表项格式、属性位(RWX/User/Kernel)

页表是虚拟内存系统的核心数据结构,负责将处理器发出的虚拟地址(VA)转换为物理地址(PA)。在 ARMv8-A 64 位架构中,采用 4KB 页面 + 4 级页表(或 64KB 页面 + 3 级页表)的经典设计。本章聚焦于最常用的 4KB 页面、4 级页表 配置,深入剖析 L0/L1/L2/L3 各级页表的职责、页表项(PTE)格式以及关键的属性位控制。

2.1 多级页表层级结构(L0/L1/L2/L3)

多级页表的核心优势在于 按需分配:只有实际映射的虚拟地址范围才需要分配页表,极大地节省了内存。对于 48 位虚拟地址空间(VA[47:0]),4 级页表将地址翻译过程分解为 4 次查表。

关键概念: 每一级页表负责解析虚拟地址中的固定位段,下一级页表的基地址由上一级页表项中的输出地址决定。最终 L3 页表项指向 4KB 的物理页面。
4级页表地址翻译流程 4级页表地址翻译流程(4KB页面,48位VA) VA[47:39] VA[38:30] VA[29:21] VA[20:12] VA[11:0] L0 页表 索引 VA[47:39] L1 页表 索引 VA[38:30] L2 页表 索引 VA[29:21] L3 页表 索引 VA[20:12] 物理地址 = PA[47:12] + VA[11:0] 说明: • L0 页表基地址由 TTBR0_EL1(用户空间)或 TTBR1_EL1(内核空间)寄存器提供。 • 每一级页表包含 512 个页表项(9 位索引,2^9 = 512),每个 PTE 占 8 字节。 • 支持块映射(Block Mapping):L1/L2 页表项可直接映射 1GB/2MB 大页,跳过后续级别。

2.2 页表项(PTE)格式详解

每个页表项(PTE)是一个 64 位的数据结构,包含下一级页表的物理地址或最终物理页面的地址,以及控制访问权限和内存属性的标志位。以 ARMv8-A 的 L3 页表项(4KB 页面) 为例,其格式如下:

位段 宽度(位) 名称 描述
[63:48] 16 PBHA / RES0 页表属性提示(Page Based Hardware Attributes)或保留位
[47:12] 36 Output Address 物理页面基地址(PA[47:12]),4KB 对齐
[11:10] 2 nG / AF 非全局位(nG)和访问标志位(Access Flag)
[9:8] 2 SH 可共享性属性(Shareability):00-非共享,01-保留,10-外部共享,11-内部共享
[7:6] 2 AP 访问权限(Access Permission):控制读写权限和特权级
[5] 1 NS 非安全位(Non-Secure),用于 TrustZone 安全扩展
[4] 1 AttrIndx 内存属性索引(Attribute Index),指向 MAIR_EL1 寄存器中的属性配置
[3:2] 2 Attr 内存属性(Memory Attributes):00-普通内存,01-设备内存,10-不可缓存,11-保留
[1] 1 nT 非表项位(Non-Table),用于区分块映射和页表项
[0] 1 Valid 有效位:1-有效,0-无效(产生缺页异常)
性能提示: 在初始化阶段,将未使用的页表项 Valid 位清零,可以避免 MMU 误解析无效地址。同时,合理设置 AF(访问标志)位可以减少缺页异常次数,提升 TLB 命中率。

2.3 属性位详解:RWX / User / Kernel

页表项中的 AP(Access Permission) 位和 PXN/UXN 位共同决定了页面的访问权限和执行权限。这些属性位是操作系统实现进程隔离和内核保护的基础。

2.3.1 访问权限(AP)位

AP 位 [7:6] 控制页面的读写权限以及访问所需的特权级:

AP[7:6] EL1(内核)访问 EL0(用户)访问 典型用途
00 读写 无权限 内核私有数据
01 读写 读写 用户共享内存
10 只读 无权限 内核代码段
11 只读 只读 用户代码段 / 只读数据

2.3.2 执行权限(PXN / UXN)位

ARMv8-A 引入了 PXN(Privileged Execute Never) 和 UXN(User Execute Never) 位,分别控制内核态和用户态的执行权限:

  • PXN(位[54]): 当置 1 时,禁止内核(EL1)在该页面上执行代码。用于防止内核执行用户空间的数据页(如栈、堆),增强安全性。
  • UXN(位[53]): 当置 1 时,禁止用户(EL0)在该页面上执行代码。用于标记数据页,防止用户态执行任意代码。
安全注意事项: 在初始化页表时,务必为所有数据页设置 PXN=1 和 UXN=1,为代码页根据特权级设置正确的执行权限。遗漏这些位可能导致权限提升漏洞或代码执行攻击。

2.3.3 其他关键属性位

  • nG(Non-Global,位[11]): 0 表示全局页面(所有进程共享,如内核空间),1 表示非全局页面(仅当前进程,TLB 中需携带 ASID)。
  • AF(Access Flag,位[10]): 0 表示页面未被访问,首次访问时 MMU 会触发访问标志异常,由软件置 1。用于页面置换算法。
  • SH(Shareability,位[9:8]): 控制多核系统中的缓存一致性域。内核空间通常设为内部共享(11),用户空间设为非共享(00)。
  • AttrIndx(位[4]): 指向 MAIR_EL1 寄存器中的 8 个内存属性条目之一,用于配置缓存策略(如写回、直写、不可缓存)。
L3 页表项属性位布局 L3 页表项(4KB页面)属性位布局 PBHA/RES0 Output Address [47:12] nG AF SH AP NS AttrIndx Attr V 关键属性位说明: • nG (Non-Global): 0=全局(内核空间),1=非全局(用户空间,需 ASID) • AF (Access Flag): 0=未访问,1=已访问(由 MMU 硬件置位或软件管理) • SH (Shareability): 00=非共享,10=外部共享,11=内部共享(多核一致性) • AP (Access Permission): 控制 EL1/EL0 的读写权限(见上表) • AttrIndx: 指向 MAIR_EL1 中的缓存策略配置(如 Normal Write-Back) • V (Valid): 0=无效(缺页),1=有效(映射存在)

2.4 页表初始化中的属性配置策略

在系统启动阶段,页表初始化代码需要为每个映射区域正确设置属性位。以下是一个典型的属性配置策略示例:

/* 页表项属性宏定义(ARMv8-A) */
#define PTE_VALID       (1UL << 0)
#define PTE_TABLE       (1UL << 1)   /* L0/L1/L2 表项 */
#define PTE_ATTR_INDEX  (2UL << 2)   /* 指向 MAIR 索引 2(Normal Write-Back) */
#define PTE_NS          (1UL << 5)   /* Non-Secure */
#define PTE_AP_RW_EL1   (0UL << 6)   /* AP=00: EL1 读写,EL0 无权限 */
#define PTE_AP_RO_EL1   (2UL << 6)   /* AP=10: EL1 只读,EL0 无权限 */
#define PTE_AP_RW_ALL   (1UL << 6)   /* AP=01: EL1/EL0 读写 */
#define PTE_AP_RO_ALL   (3UL << 6)   /* AP=11: EL1/EL0 只读 */
#define PTE_SH_INNER    (3UL << 8)   /* 内部共享 */
#define PTE_AF          (1UL << 10)  /* 访问标志置位 */
#define PTE_NG          (1UL << 11)  /* 非全局 */
#define PTE_UXN         (1UL << 53)  /* 用户不可执行 */
#define PTE_PXN         (1UL << 54)  /* 特权不可执行 */

/* 内核代码段:EL1 只读,不可执行(PXN=0, UXN=1) */
#define ATTR_KERN_CODE  (PTE_VALID | PTE_AF | PTE_SH_INNER | \
                         PTE_AP_RO_EL1 | PTE_UXN)

/* 内核数据段:EL1 读写,不可执行 */
#define ATTR_KERN_DATA  (PTE_VALID | PTE_AF | PTE_SH_INNER | \
                         PTE_AP_RW_EL1 | PTE_UXN | PTE_PXN)

/* 用户代码段:EL0 只读,可执行 */
#define ATTR_USER_CODE  (PTE_VALID | PTE_AF | PTE_NG | \
                         PTE_AP_RO_ALL)

/* 用户数据段:EL0 读写,不可执行 */
#define ATTR_USER_DATA  (PTE_VALID | PTE_AF | PTE_NG | \
                         PTE_AP_RW_ALL | PTE_UXN | PTE_PXN)
性能优化要点:
  • AF 位预置: 在初始化阶段直接将 AF 位置 1,避免后续访问时产生访问标志异常,减少 TLB 缺失开销。
  • SH 位选择: 内核空间使用内部共享(SH=11),确保多核缓存一致性;用户空间使用非共享(SH=00),减少不必要的缓存一致性广播。
  • AttrIndx 配置: 将 MAIR_EL1 索引 0 配置为不可缓存(用于设备内存),索引 2 配置为写回(用于普通内存),索引 4 配置为直写(用于帧缓冲)。

2.5 块映射(Block Mapping)与性能

多级页表支持 块映射,允许 L1 或 L2 页表项直接映射大块连续内存,跳过后续级别的页表查找。这能显著减少 TLB 压力并提升性能:

页表级别 块大小 虚拟地址位段 适用场景
L1 块映射 1 GB VA[47:30] 大块设备内存映射、GPU 缓冲区
L2 块映射 2 MB VA[47:21] 内核线性映射区域、大页(HugeTLB)
L3 页映射 4 KB VA[47:12] 标准页面、进程私有映射
优化建议: 在页表初始化阶段,对于内核的线性映射区域(如 DDR 内存的连续大块),优先使用 L2 块映射(2MB 大页)。这可以将 TLB 覆盖范围提升 512 倍,显著降低 TLB 缺失率。对于 I/O 外设区域,使用 L1 块映射(1GB)可进一步减少页表层级遍历。

通过合理配置多级页表的层级结构、精确设置页表项属性位以及利用块映射特性,嵌入式系统可以在启动阶段构建一个高效、安全且可扩展的虚拟内存映射,为后续的 MMU 使能和操作系统运行奠定坚实基础。