少于 1 分钟阅读 次阅读

概要:说明“每个进程一份页表”是进程隔离的核心手段:用户空间低地址映射各自独立,内核空间高地址映射共享,并通过修改 %cr3 寄存器实现页表切换。

在绝大多数现代操作系统中,每个进程(应用)都拥有自己独立的一份页表。

这是操作系统实现“进程隔离”最核心的手段。如果所有应用共用一份页表,那 A 应用就能轻而易举地读写 B 应用的数据,电脑就会变成一个大乱炖,毫无安全性可言。

1. 独立的地基:用户空间 (User Space)

每个进程的页表在低地址部分(从 0 开始往上)的映射是完全不同的。

  • 进程 A 的页表:把虚拟地址 0x1000 映射到物理内存的第 50 号页框。
  • 进程 B 的页表:把虚拟地址 0x1000 映射到物理内存的第 90 号页框。

结果:虽然 A 和 B 都在操作 0x1000 这个地址,但由于它们使用的是不同的页表,硬件最终访问的是物理内存中完全不同的两个地方。


2. 共享的天空:内核空间 (Kernel Space)

虽然每个进程都有独立的页表,但这些页表有一个共同点:它们的高地址部分0x80000000 以上)的映射通常是一模一样的。

  • 为什么要一样? 因为内核只有一份。无论当前运行的是哪个进程,只要它发起系统调用进入内核,看到的内核代码和硬件驱动都应该是同一套。
  • 如何实现? 当操作系统创建一个新进程时,它会把“内核页表”的内容直接复制到新进程页表的高地址项中。

比喻:每个进程都是一座独立的“孤岛”(用户空间),但所有孤岛抬头看到的都是同一片“天空”(内核空间)。


3. 硬件是如何切换的? (%cr3 寄存器)

既然有这么多份页表,CPU 怎么知道现在该用哪一份呢?

这就是内核中 “进程切换(Context Switch)” 的工作了:

  1. 当 CPU 从进程 A 切换到 进程 B 时。
  2. 内核会找到进程 B 的 struct proc 结构体,取出其中的 p->pgdir(这是进程 B 页表的物理首地址)。
  3. 内核执行一条特殊的指令,把这个地址加载到 CPU 的 %cr3 寄存器中。
  4. 一旦 %cr3 被更新,整个世界的翻译规则瞬间改变。从那一刻起,所有的虚拟地址都会按照进程 B 的页表进行翻译。

总结

维度 情况 目的
页表数量 每个进程一份 确保每个进程有独立的地址空间。
映射内容 低地址独有,高地址共享 独有部分实现隔离,共享部分实现内核统一访问。
切换机制 修改 %cr3 寄存器 让硬件实时跟随当前运行的进程进行翻译。

冷知识:这种“每个进程一份页表”的设计虽然安全,但也有代价。如果系统里有 1000 个进程,就要存 1000 份页表,这会消耗不少内存。所以现代 CPU 引入了多级页表(Multi-level Page Table)来节省空间,只给真正用到的地址分配页表项。

留下评论