3. 页表与转换表基础
好,咱们今天聊聊页表。这东西说白了就是 MMU 的「导航地图」。CPU 发出的虚拟地址怎么变成物理地址?全靠页表来翻译。
我记得刚接触 ARMv7 的时候,总觉得页表就是个简单的查表过程。后来踩了坑才发现——页表的结构、粒度、层级关系,每一个细节都直接影响系统性能和稳定性。
3.1 页表的概念
页表(Page Table)是一张映射表。它记录了虚拟地址到物理地址的转换关系。每个条目叫页表项(PTE),里面存着物理页框号、权限位、属性位等信息。
你想想看,如果整个 4GB 地址空间只用一张表来映射,那这张表得有多大?4GB / 4KB = 1M 个条目。每个条目 4 字节,光这一张表就要 4MB。这在嵌入式系统里太奢侈了。
所以 ARMv7 用了多级页表。常见的是两级结构:一级页表 + 二级页表。这样大部分内存不需要映射,页表可以按需分配,省空间。
3.2 一级页表与二级页表
一级页表也叫 L1 页表。它把 4GB 地址空间分成 4096 个条目,每个条目覆盖 1MB 的范围。L1 页表项可以指向一个 1MB 的段(Section),也可以指向一个二级页表。
二级页表也叫 L2 页表。它把 1MB 的范围再细分成更小的页。根据你选的粒度,可以分成 256 个 4KB 页,或者 64 个 16KB 页,或者 16 个 64KB 页。
我在项目中遇到过一种情况:某个外设的寄存器区域只有 8KB,但用段映射直接占了 1MB 的虚拟地址空间。这太浪费了。后来改成二级页表,用 4KB 粒度映射,只占 8KB 虚拟空间,干净利落。
| 层级 | 覆盖范围 | 条目数 | 典型用途 |
|---|---|---|---|
| L1 页表 | 4GB | 4096 | 粗粒度索引,段映射 |
| L2 页表 | 1MB | 256 / 64 / 16 | 细粒度映射,小内存区域 |
3.3 页表项(PTE)结构
页表项的结构决定了你能控制什么。ARMv7 的 PTE 里包含这些关键字段:
- 物理页框号(PFN):物理地址的高位部分,决定数据到底存在哪块物理内存里。
- 访问权限位:AP[2:0],控制读/写/执行权限。我习惯把内核空间设成特权级访问,用户空间只给读和执行。
- 域(Domain):ARMv7 特有的权限控制机制。每个域可以独立配置访问检查策略。
- 缓存属性位:C(Cacheable)、B(Bufferable)、TEX[2:0],控制是否缓存、写策略等。
- 共享属性位:S(Shareable),多核系统里必须注意这个位。设错了会导致缓存一致性问题。
- XN 位:Execute Never,禁止执行。对付缓冲区溢出攻击的利器。
3.4 转换粒度
ARMv7 支持三种页粒度:4KB、16KB、64KB。选哪个?看你的场景。
| 粒度 | L2 条目数 | TLB 覆盖范围 | 适用场景 |
|---|---|---|---|
| 4KB | 256 | 小 | 精细控制,外设寄存器,小内存块 |
| 16KB | 64 | 中 | 通用内存映射,平衡粒度与 TLB 效率 |
| 64KB | 16 | 大 | 大块连续内存,减少页表占用 |
我个人习惯:代码段和数据段用 64KB 粒度,TLB 命中率高。外设寄存器区域用 4KB 粒度,精确控制访问权限。16KB 用得少,除非有特殊需求。
3.5 页表结构总览
下面这张图展示了 ARMv7 两级页表的完整转换流程。从虚拟地址出发,经过 L1 索引、L2 索引,最终找到物理地址。
嗯,这张图把整个流程串起来了。从虚拟地址出发,先拆成 L1 索引、L2 索引和页内偏移。然后查 L1 页表找到 L2 页表的基地址,再查 L2 页表找到物理页框号,最后加上页内偏移得到物理地址。
3.6 页表配置实战要点
配置页表时,有几个地方容易出问题:
- 页表对齐:L1 页表必须 16KB 对齐,L2 页表必须 1KB 对齐。不对齐的话 MMU 直接罢工。
- 页表内存属性:页表本身所在的内存必须配置为 Inner Write-Back, Write-Allocate。否则查表性能会很难看。
- TLB 维护:修改页表后必须做 TLB 失效操作。我习惯用
DSB + ISB保证修改生效。 - 域访问控制:每个域有 2 位控制位,可以设成 No Access、Client、Manager。Manager 模式跳过权限检查,适合内核关键区域。
DSB,再 TLBIALL,再 ISB,一步不落。
页表是 MMU 的基石。理解了一级二级结构、PTE 字段含义、粒度选择,后面配置页表、调试异常就顺了。别急,慢慢来,先把这些基础打牢。