1. 页表基础概念
1.1 页表的作用
页表(Page Table)是操作系统为每个进程维护的一种数据结构,用于将虚拟地址映射到物理地址。它是分页内存管理的核心组件,承担着地址转换、权限控制和物理内存隔离的关键职责。
核心作用:页表使得每个进程拥有独立的虚拟地址空间,进程之间互不干扰,同时允许物理内存被多个进程共享(如共享库、共享内存段)。
页表在地址转换中的位置如下图所示:
1.2 页表项结构
页表由多个页表项(Page Table Entry, PTE)组成,每个PTE对应一个虚拟页到物理页框的映射。一个典型的PTE包含以下字段:
| 字段 | 位宽(典型) | 说明 |
|---|---|---|
| 物理页框号(PFN) | 20~24 bit | 物理内存中页框的基地址,与页内偏移拼接形成完整物理地址 |
| 有效位(Valid) | 1 bit | 表示该页表项是否有效,0表示该虚拟页未映射到物理内存 |
| 读/写位(R/W) | 1 bit | 0表示只读,1表示可读写 |
| 用户/超级用户位(U/S) | 1 bit | 0表示仅内核态可访问,1表示用户态也可访问 |
| 存在位(Present) | 1 bit | 表示该页是否在物理内存中(与Valid位功能类似,不同架构命名不同) |
| 脏位(Dirty) | 1 bit | 表示该页是否被写入过,用于页面置换时的回写判断 |
| 访问位(Accessed) | 1 bit | 表示该页是否被访问过,用于页面置换算法 |
| 缓存禁用位(PCD/PWT) | 2 bit | 控制该页的缓存策略(是否可缓存、写透/写回) |
架构差异:x86-64架构的PTE大小为8字节(64位),其中高12位保留给未来扩展;ARMv8-A的PTE同样为64位,但字段布局不同。RISC-V架构的PTE为64位,采用Sv39/Sv48分页方案。
一个典型的32位x86页表项结构如下:
1.3 页表在地址转换中的位置
地址转换过程是CPU执行指令时的关键路径,页表位于MMU(内存管理单元)与物理内存之间,具体流程如下:
- CPU发出虚拟地址:程序计数器(PC)或访存指令生成一个虚拟地址(VA)。
- MMU截获虚拟地址:MMU将虚拟地址拆分为虚拟页号(VPN)和页内偏移(Offset)。
- 查询页表:MMU以VPN为索引,在页表中查找对应的PTE。
- 检查权限与有效性:MMU检查PTE中的有效位、读写位、用户/超级用户位等,判断访问是否合法。
- 生成物理地址:将PTE中的物理页框号(PFN)与页内偏移拼接,形成完整的物理地址(PA)。
- 访问物理内存:MMU将物理地址发送到内存控制器,完成数据读取或写入。
关键性能瓶颈:每次地址转换都需要一次额外的内存访问(读页表),这会使访存延迟加倍。如果页表不在TLB(快表)中缓存,一次地址转换可能引发多次内存访问(多级页表场景),严重拖慢程序执行速度。TLB缺失处理将在后续章节详细讲解。
地址转换的完整数据流如下图所示:
1.4 页表的关键设计考量
| 设计维度 | 问题 | 解决方案 |
|---|---|---|
| 空间开销 | 32位系统需要约4MB页表(每个进程),64位系统页表大小呈指数增长 | 多级页表、倒排页表、哈希页表 |
| 时间开销 | 每次地址转换需访问内存读页表,增加访存延迟 | TLB(快表)缓存最近使用的页表项 |
| 进程切换 | 不同进程使用不同页表,切换时需刷新TLB | TLB标记(ASID)、进程上下文标识 |
| 大页支持 | 4KB小页导致页表项过多,TLB覆盖范围有限 | 2MB/1GB大页(Huge Pages)、透明大页 |
| 权限控制 | 需要细粒度控制内存访问权限 | PTE中的R/W、U/S、NX(不可执行)位 |
实践建议:在操作系统实现中,页表通常采用惰性分配策略——只在进程实际访问某个虚拟页时才分配对应的物理页框并建立页表映射。这样可以避免为未使用的虚拟地址空间浪费物理内存和页表项。