笔者愚钝,在页表自映射这地方花了很长时间,幸得大佬指点迷津才得以理解。

# 目的

在普通的分页机制中,页目录和页表只在物理内存中出现,而程序只能通过虚拟地址对内存进行操作。这就导致了操作系统难以访问和修改它们。

于是产生了页表自映射机制来解决这一问题。自映射通过 将页目录和页表映射到固定的虚拟地址,使得:

内核可直接通过虚拟地址修改页表,无需临时映射或关闭分页,像访问普通内存一样操作页表即可。

# 实现

image-20250417220533361

共有 1024 个二级页表(以下简称页表),每个页表记录了 1024 条映射关系,大小为 4KB 。这些页表物理上不连续,但映射到虚拟空间是连续的,共占 4MB 的空间(并且 4MB 对齐)

注意到,页目录记录了 1024 个页表的物理页号,它和页表在格式上完全一致。故为了节省 4KB 的空间,我们将页目录也作为这 4M 中的一个页表。既然 页目录同时作为页表 ,那么它需要 有一个页目录项来记录自己(作为页表)的物理页号。这便是页目录的自映射

于是乎,页目录便有了三重身份:页目录、页表、普通的物理页

# 地址计算

页目录应该位于这 4MB 中的什么位置呢?自映射的页目录项又应该在什么位置呢?这是两个值得探讨的问题,笔者当时正是在此处陷入困境

推导过程可用一句话概括:由一级偏移量 得到 二级偏移量 和 页内偏移量

image-20250417231632119

设页表空间的虚拟基址为 PTbase ,页目录的虚拟基址为 PTbase 。由于页表空间是 4MB 对齐的,必有

PTbase = (PTbase >> 22 ) << 22		// 即 PTbase 的低 22 位全部为 0

因此 PDbase 的高 10 位与 PTbase 相同。根据虚拟地址结构,这便是 页目录自映射项的偏移量(一级偏移量,按字偏移)

由于 页目录同时也作为页表,所以其二级偏移量也是 PTbase[31 : 22] (按字偏移)

PDbase4KB 对齐的,所以低 12 位为 0。得到公式:

PDbase = PTbase | (PTbase >> 10)

而自映射项的虚拟地址,只需要加上页内偏移量即可。

由于页目录同时也作为普通页,所以自映射项的偏移量与一级、二级偏移量相同,但是 按字节偏移。

PDEself = PTbase | (PTbase >> 10) | (PTbase >> 20)
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

CircleCoder 微信支付

微信支付

CircleCoder 支付宝

支付宝

CircleCoder 贝宝

贝宝