Skip to content

第五十七章:页表经脉

元婴初期

涉及内核源码:

林小源蹲在一块 VMA 大陆的边缘,盯着脚下的"地面"看。

地面是半透明的,像冰层。冰层下面有东西在流动——不是水,是一串串发光的数字。他把脸凑近,看到那些数字排列成整齐的表格,每一行都指向另一个位置。

"别趴在地上。"mm_struct 的声音从远处飘来,带着不耐烦,"你在看的是页表。"

"页表?"

"虚拟地址到物理地址的翻译表。"mm_struct 说,"你看到的每个虚拟地址,都要经过页表翻译才能变成真实的物理地址。你现在踩的这块地,虚拟地址是 0x00400000,翻译之后对应的物理地址——"它停了一下,"你不需要知道。"

林小源站起来,但眼睛还盯着地面。那些数字在他脚下流动,像某种活的东西。他注意到每一行数字都分成两部分——前面一部分是地址,后面一部分是一串标记。

"后面那些标记是什么?"

"标志位。"mm_struct 说,"VRWXU——每一个 bit 都是一道锁。V 表示这个页表项有效,R 可读,W 可写,X 可执行,U 用户态可访问。少一个 bit,你就进不去。"

林小源试着用脚踩了一下地面。脚底传来一阵微弱的震动,像是什么东西被激活了。

"你刚才踩的那一下,触发了一次地址翻译。"mm_struct 说,"硬件读取你的虚拟地址,查三级页表,算出物理地址。整个过程——三次内存访问。"

"三次?"

"Sv39 页表,三级结构。"mm_struct 的声音里带了一丝讲解的意味,"虚拟地址 39 位有效,分成四段——VPN[2]、VPN[1]、VPN[0],各 9 位,加上 12 位页内偏移。每一级页表用一段 VPN 做索引,找到下一级的地址。三次查找,三次内存访问。"

林小源低头看自己的脚。脚底的地面变得透明了一些,他隐约能看到下面有三层结构——三层表格,层层嵌套,像某种精密的机械在运转。

"三次太慢了。"他脱口而出。

mm_struct 没有说话,但林小源感觉到它在笑。

林小源在内景中走了很久,渐渐注意到了一些奇怪的细节。

每当他走过一段路,脚下的地面就会变亮一点。而那些他没走过的区域,地面是暗的。

"那些变亮的是什么?"

"Accessed 位。"mm_struct 说,"你每走一步,硬件就自动把对应页面的 A 位置 1。内核定期扫描这些位——A 位为 0 的页面,说明很久没被访问了,可以被回收。"

林小源蹲下来,仔细看一块特别亮的地面。除了 A 位,他还看到一个 D 位也是 1。

"D 是 Dirty。"mm_struct 解释,"页面被写入时,硬件自动置 1。内核回收页面时,D 位为 1 的需要写回磁盘,D 位为 0 的可以直接丢弃。"

"硬件自动设置?"

"对。你不需要做任何事——走路、触碰、思考,硬件都在帮你记录。"mm_struct 的声音变得低沉,"但你也要小心。这些位一旦被设置,就会影响内核的决策。一个被频繁访问的页面不会被回收,一个被写脏的页面需要额外的 IO。你的每一个动作,都在改变系统的命运。"

林小源站起来,看着脚下延伸开去的地面。每一块都在发光,明暗不同,像一片星空。

"这下他懂了——他的每一步都有人在记录。"

"不是记录。"mm_struct 纠正他,"是翻译。页表把你的虚拟世界翻译成真实世界。没有页表,你连一步都走不出去。"

林小源继续走着,突然感觉脚下一滑。

他低头一看——地面消失了。不是被遮挡,是真的消失了。他脚下是一片虚无,什么都没有。

"你走到了页表的边界。"mm_struct 说,"这里没有映射。你的虚拟地址没有对应的物理地址。"

林小源往后退了一步,地面重新出现。他心跳加速。

"这就是 segfault。"mm_struct 说,"当你访问一个没有映射的虚拟地址,硬件查页表发现没有对应的 PTE,就会触发 page fault。如果这个地址确实不应该被访问——内核会发送 SIGSEGV,你的进程就死了。"

林小源咽了口唾沫。

"但也不是所有的 page fault 都是坏事。"mm_struct 继续说,"有些页面被换出到磁盘了,访问时会触发 minor fault,内核把页面换回来。有些页面是 demand paging 的——第一次访问时才分配物理页,触发 major fault。"

"所以 page fault 有时候是——"

"是正常的。"mm_struct 说,"只有当你访问不该访问的地址时,page fault 才是致命的。"

林小源看着脚下的地面,心里多了一层敬畏。这片看似坚实的土地,其实是由无数个页表项支撑的。每一步都是一次翻译,每一次翻译都可能失败。

"TLB。"mm_struct 突然说。

"什么?"

"你刚才问三次内存访问是不是太慢了。"mm_struct 说,"TLB 是答案。Translation Lookaside Buffer——页表的缓存。最近使用的翻译结果存在 TLB 里,下次访问同一个页面时,直接从 TLB 取结果,一次内存访问就够了。"

林小源感觉到脚下的地面变得温暖了。那不是温度的变化,是某种效率的提升——TLB 命中时的感觉。

"缓存是加速的关键。"他轻声说。

"不。"mm_struct 说,"缓存是信任的结果。TLB 缓存的是页表的翻译结果——如果页表变了,TLB 必须被刷新。否则你会访问到错误的物理地址。"

林小源沉默了。他想起之前在调度器那边学到的——信任是需要验证的。缓存是信任,但刷新缓存是验证。

他忽然明白过来——页表不只是翻译,还是一种约束。


c
/* Sv39 页表结构(RISC-V 64 位) */
#define PTE_V (1 << 0)
#define PTE_R (1 << 1)
#define PTE_W (1 << 2)
#define PTE_X (1 << 3)
#define PTE_U (1 << 4)
#define PTE_A (1 << 6)
#define PTE_D (1 << 7)

printf("=== Sv39 页表 — 地址翻译的经脉 ===\n\n");

unsigned long va = 0x0000003FCA123456;
printf("虚拟地址: 0x%016lx\n\n", va);

unsigned long vpn2 = (va >> 30) & 0x1FF;
unsigned long vpn1 = (va >> 21) & 0x1FF;
unsigned long vpn0 = (va >> 12) & 0x1FF;
unsigned long offset = va & 0xFFF;

printf("VPN[2] (L2 索引): %lu (0x%03lx)\n", vpn2, vpn2);
printf("VPN[1] (L1 索引): %lu (0x%03lx)\n", vpn1, vpn1);
printf("VPN[0] (L0 索引): %lu (0x%03lx)\n", vpn0, vpn0);
printf("页内偏移:         %lu (0x%03lx)\n\n", offset, offset);

unsigned long pte = 0x200000CF;
printf("PTE: 0x%016lx\n", pte);
printf("  PPN: 0x%09lx\n", (pte >> 10) & 0xFFFFFFFFF);
printf("  V=%lu R=%lu W=%lu X=%lu U=%lu A=%lu D=%lu\n",
       (pte >> 0) & 1, (pte >> 1) & 1, (pte >> 2) & 1,
       (pte >> 3) & 1, (pte >> 4) & 1, (pte >> 6) & 1,
       (pte >> 7) & 1);

printf("\n--- 页表翻译过程 ---\n");
printf("1. 读取 satp 寄存器,获取根页表地址\n");
printf("2. 用 VPN[2] 索引 L2 页表,获取 L1 页表地址\n");
printf("3. 用 VPN[1] 索引 L1 页表,获取 L0 页表地址\n");
printf("4. 用 VPN[0] 索引 L0 页表,获取物理页号\n");
printf("5. 物理地址 = PPN << 12 | offset\n");

printf("\n--- 页大小 ---\n");
printf("4KB 页:  三级页表\n");
printf("2MB 大页: 二级页表(跳过 L0)\n");
printf("1GB 大页: 一级页表(跳过 L0 和 L1)\n");

道藏笔记

内核启示

Sv39 页表是 RISC-V 64 位系统的地址翻译机制。虚拟地址 39 位有效,分成四段:VPN[2]VPN[1]VPN[0] 各占 9 位做页表索引,剩下 12 位是页内偏移。三级页表,三次查找,三次内存访问——听着慢,但有 TLB 兜底。

PTE 里的标志位是一套精密的权限锁:V 标记有效,R/W/X 控制读写执行,U 区分用户态和内核态。A(Accessed)和 D(Dirty)两个位由硬件自动设置——你每走一步,A 位就亮一下;你写入数据,D 位就跟着变。内核靠这两个位来判断页面的活跃程度和是否需要写回磁盘。

页面大小有三种选择:4KB 用三级页表,2MB 大页只需要二级,1GB 大页只需一级。TLB 是页表的缓存——最近用过的翻译结果存在里面,命中时直接取结果,省去了三次内存访问。页表是内存管理的"经脉"——虚拟与物理的桥梁。


破关试炼

页表经脉之试

页表翻译虽然精确但太慢,本章提到用来缓存地址翻译结果的硬件结构是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道