Skip to content

pgtbl:只读共享页、页表可视化与 2MiB 大页 ​

版本:pgtbl,提交 cff452f8f70f46e9b22f815fc879242ebe5eacd6。本实验包含四组工作:解释现有页表、加速 getpid、打印页表、实现 superpage。先完成内存的手算翻译,再写内核。

从真实输出解释权限 ​

运行 pgtbltest,它通过已有 pgpte 接口观察用户页表。逐项说明:虚拟地址处是什么对象、映射到哪页、PTE flags 的各位意味着什么。不同运行的物理地址不必相同,不能用地址完全一致判断正确。重点区分用户文本、数据、guard、TRAPFRAME 和 TRAMPOLINE 的权限。

例如 0x13 的低位组合是 V、R、U,没有 W/X,适合用户只读数据页。不是所有“看起来像用户地址”的高端映射都应该带 U:用户能写 trapframe 会破坏内核恢复现场的可信性。

USYSCALL:分配、映射与释放成套设计 ​

为每个进程准备一页,写入 struct usyscall 的 PID,并在 USYSCALL 映射成用户只读。ugetpid() 已提供,读取这个映射即可。

按生命周期实施:在创建进程时分配并初始化;proc_pagetable() 插入映射;构造失败时逆序撤销;销毁时解除映射并恰好释放一次。参考 trapframe 路径但不要逐字复制,因为 trapframe 没有 PTE_U,而 USYSCALL 必须允许用户读。

先单独通过 ugetpid,再做后续。若 fork 后 PID 错误,检查有没有误共享父进程内容;若反复 fork 后耗尽内存,检查失败路径与 freeproc;若用户写入该页成功,检查 PTE_W。

vmprint:递归时累积虚拟地址 ​

每层 index 决定地址中的不同九位。递归传入当前前缀,遇到第 level 层的第 i 项,将 i << (12+9*level) 合入前缀。打印所有有效项,但只对非叶项递归。按层缩进,并打印完整 64 位值,便于看清结构。

2025 的输出格式包含该项对应的虚拟地址,不是旧教程常见的“只打印 index”。函数入口已经由 kpgtbl 连接;不需要另外发明一个启动时打印所有进程的接口。

大页需要改变分配单位 ​

先在物理内存分配器中保留少量对齐的 2MiB 连续区域,用 superalloc/superfree 管理,普通 4KiB freelist 不应再次包含这些页。随后沿 sys_sbrk → growproc → uvmalloc 找到扩张路径,把能完整覆盖的对齐区间用 level-1 叶项表示,两头零散区间保留普通页。

假设新增长区间为 [0x180000,0x500000):前面 [0x180000,0x200000) 用普通页,中间 [0x200000,0x400000) 可用一张大页,尾部 [0x400000,0x500000) 不满 2MiB,仍用普通页。不能只因为总长度超过 2MiB 就从起点直接映射大页。

本分支 walk() 已含 PTE_LEAF 判断,可提前返回高层叶项;但 walkaddr() 原样仅返回 PTE 中物理基址。若调用者需要普通页粒度的地址,必须考虑大页内部偏移,否则跨 4KiB 访问可能总落到大页开头。逐一审查 copyin/out、uvmcopy、uvmunmap 等消费者对“walk 总返回 level-0”的假设。

fork、释放与 demotion ​

uvmcopy 必须识别源映射大小并建立正确的子映射,不能把大页当页表递归,也不能只复制前 4KiB。uvmunmap 完整解除大页时释放 2MiB 块;部分解除则需要降为普通映射。

一种清楚的降级设计是准备末级页表,把大页中的 512 个普通页分别建立映射,再替换原 level-1 叶项,之后按普通页释放目标范围。这里有一个分配器陷阱:拆出来的 4KiB 片段不能既回到普通 freelist,又仍被 superfree 当成完整大块。必须定义拆分后所有权转移规则,并确保后续释放不会重叠。

失败路径也要保持原映射可用:新页表没分配成功前,不破坏旧大页;安装完成后考虑 TLB 刷新;部分释放后仍保留的页面内容必须完整。superpg_free 特别检查末尾 4KiB 被释放后,前一页的数据还存在。

验收 ​

运行 pgtbltest、usertests -q、make grade。模型中的 2MiB 地址翻译测试只覆盖索引与偏移,不覆盖真实分配器的碎片、demotion、fork 回滚或 TLB。自己补充“恰好对齐”“只差一页”“先大页后缩小再增长”的场景。

自测:512 个 4KiB 页物理上连续,就可以自动当作大页吗?

还必须满足虚拟与物理起点对齐、权限一致、PTE 处于正确层级,以及引用和释放粒度可兼容。连续只是必要条件之一。

来源:2025 pgtbl 官方任务,固定分支中的 vm.c/riscv.h/proc.c/kalloc.c/user/pgtbltest.c。