Skip to content

三张表,和门后必须有路 ​

咱们动工的头一件事是安家。三张表的住处其实早就定了:咱们把 boot 的低内存一次画全的那一回,就在 0x1000 到 0x4000 之间圈了一块 12KB 的宅基地,kPageTables 就是它的名字,咱们在断言里连“必须 4K 对齐是长模式的硬要求”都写明了。那块地一空就空到了今天,到了本站,它终于迎来了头一批住户。为什么偏偏是 0x1000、不住 0x0?咱们看低内存的头一页就明白了。0x0 起的头 1KB,是实模式中断向量表的遗址。A20 那回咱们见过:门一锁、地址一折,就有地址“一发命中了中断向量表”,说的就是这 1KB。紧挨着它的,还有 BIOS 自己的数据区,跟向量表一样都是实模式住下的老住户,而空指针解引用,落的也是这一页。咱们把头一页空着不派用场,给将来留的,是一个炸也炸在门牌 0 上的整齐。

咱们再往深里看一层:长模式的页表,正经的建制是四级:PML4、PDPT、PD、PT,一层套一层地往下指。咱们不摆四级,因为有个省事的机关,它的名字叫大页。PD 里的一项要是带上了 Large 位,它就不再往下指 PT 了,而是自己直接管一整页 2MB 的地界。所以三张表加四个大页,就是咱们的最小组。PML4 的家在 0x1000,PDPT 的家在 0x2000,PD 的家在 0x3000,各占一整页的 4KB,而一页正好装下 512 个 8 字节的项。

每颗项 8 字节的宽度,也不是咱们随手挑的:长模式用的表格式叫 PAE,中文的名字是物理地址扩展,表项 8 字节、地址字段比老格式宽,这些都定死在 PAE 的格式里,而长模式就长在这套格式的顶上。所以后面的切换序列里,咱们在开分页之前还得把 CR4 里的 PAE 位拨开——不开 PAE,长模式是申请不到的。

层级感咱们掂一下就够:PML4 的一项管 512GB,PDPT 的一项管 1GB,而 PD 里的一项大页管 2MB。咱们只点亮 PML4 的第 0 项、PDPT 的第 0 项,再点 PD 的前四项。至于四级表的完整展开、更高处的映射,那都是后面内存管理正餐里的事,咱们本站一个也用不上。

四项大页铺的是 0 到 8MB,而且虚拟地址会原样地落到同一个物理地址上。这套铺法的名字叫恒等映射。为什么非恒等不可?咱们回头想推门那一拍:PG 亮起来的那一拍,CPU 立刻拿正在执行的下一条指令的地址,去走咱们刚立好的表。而咱们的代码、栈和三张表自己,眼下全都住在低区的地界里,PG 亮起的那一拍执行流还在这一边。所以咱们要是不把低区原样铺过去,下一条指令当场就走丢了,等着它的就是三重故障。

咱们拿一个真地址走一遍表,您就看清 CPU 眼里的路了。走表的起点是 CR3:它存着 PML4 的位置,本卷后面咱们亲手把它装进去。取虚拟地址 0x400123:

text
0x400123 >> 39 = 0   → 查 PML4[0]  → 指向 0x2000 的 PDPT
0x400123 >> 30 = 0   → 查 PDPT[0]  → 指向 0x3000 的 PD
0x400123 >> 21 = 2   → 查 PD[2]    → 大页,基址 0x400000
0x400123 低 21 位 = 0x123  → 页内偏移
0x400000 + 0x123 = 0x400123   → 物理地址,还是它自己

咱们把这一趟数下来,它的全部就是三步查表加一步拼偏移。而 CPU 的每一条指令、每一次内存访问,背后其实都是这样一趟。表要是填错了,这一趟就走岔了。而表要是空着,那就连路都没有了。最后一行算出来的物理地址,跟当初输入的虚拟地址是同一个数,恒等映射指的就是原样落回原处。

为什么一铺就是 8MB?咱们算的是下一站的活:过桥的内核要几 MB 的地界安身,咱们现在把大页多铺几张,下一站就不用回头改表了。何况 PDPT[0] 本来就管着整整 1GB 的地界,咱们点它的前四项,花的不过是四行填表的循环。

还有一层顺带的罩护,咱们提一句:三张表住的 0x1000 到 0x4000、整段都在第一张大页里,而 PG 亮起之后,咱们按老地址照样摸得到它们——CPU 走表走的是物理地址、本来就不靠这层映射,可咱们将来要改表、要读表,手里拿的还是脚下的地址,这层罩护是给软件用的。

最后把本站的地界说清楚:咱们只铺够用的最小组,咱们的任务是把 PG 开起来、把执行流送过去,本站就算交了差。至于四级表怎么长、映射怎么分的事,那都归后面的内存管理去操心,眼下还轮不到咱们。

r05_long_mode-2-gfc5569b · fc5569b · 2026-10-01