x86 4级分页机制下管态虚拟地址如何完成物理地址转换
4级分页高16位非零虚拟地址的转换逻辑
你对Intel 4级分页规则的理解存在偏差:手册提到的“仅使用虚拟地址低48位完成地址转换”,不代表高16位必须全为0,4级分页模式下CPU对线性地址的强制要求是必须为规范地址(Canonical Address):即地址的第48位到63位,必须和第47位的值完全一致——如果第47位是0,高16位必须全0;如果第47位是1,高16位必须全1。不符合这个规则的地址会直接触发通用保护异常(#GP)。
你在GDB中看到的内核地址0xffffffff812eccc0完全符合规范地址要求:
(gdb) x/g 0xffffffff812eccc0 0xffffffff812eccc0 <get_page_from_freelist>:
该地址第47位为1,高16位全为1,满足符号扩展要求。CPU做页表遍历的时候,会直接忽略高16位的值,仅取低48位走常规四级转换流程:依次查询PML4表、PDPT表、PD表、PT表,最终得到物理地址,不存在和手册规则矛盾的问题。
Linux将内核空间统一放在第47位为1的高地址段、用户空间放在第47位为0的低地址段,只是操作系统层面的地址空间划分设计,和硬件分页逻辑无关。
高端内存(High Memory)相关疑问
你在Intel SDM里搜不到“high memory”相关内容是正常的,因为高端内存是操作系统内核定义的软件概念,不是x86硬件层面的术语,CPU硬件本身没有这个定义,自然不会出现在架构手册里。
维基百科定义里提到的“未被操作系统内核页表直接映射”,指的是没有做内核启动时就固定好的永久线性映射,不是指访问时没有对应页表。
这个概念最早来自32位x86系统的设计限制:32位模式下虚拟地址空间总共只有4G,Linux默认把最高1G划给内核使用,其中前896M的内核虚拟地址空间会和物理内存的前896M做一一对应的固定映射,物理地址和虚拟地址只差一个固定偏移,访问时不需要临时修改页表,效率极高,这部分被固定映射的物理内存就是低端内存。
如果机器物理内存超过896M,超出的部分没法在仅1G大小的内核地址空间里全部预留永久映射位置,这部分物理内存就被划分为高端内存。内核需要访问高端内存的时候,会从预留的临时虚拟地址窗口里划出一段,动态建立对应页表映射,访问完成后可以释放该映射给其他高端内存页复用,整个地址转换过程全程有合法页表支撑,不存在“无页表完成地址转换”的情况。
64位模式下内核可用的虚拟地址空间达到百TB级别,完全可以把所有安装的物理内存都做永久固定映射,因此64位Linux实际上已经不存在32位时代的高端内存访问限制,只是代码中保留了相关兼容逻辑。
内容的提问来源于stack exchange,提问作者Some Name

