Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

页表

复习

  • 地址重定位与内存保护:程序不知道实际装在哪里时怎样访问数据
  • 分段:按代码、数据和栈划分内存,以及外部碎片从何而来
  • 页与分页:把虚拟内存和物理内存都切成固定大小的块

TL;DR

  • 页表(page table)记录“虚拟页对应哪个物理页帧”
  • 它由内核维护,每个进程一份
  • 页表项里还有有效位、权限位等信息
  • 页表本身可能很大,这是后面要解决的新问题

正文

  分页把内存切成整齐的页,可页往往不连续地散落在物理内存各处。于是需要一个“对照表”,记录每一页到底被放到了哪个页帧。这张表就是页表(page table)。

一张对照表

  页表可以想成一个数组:用虚拟页号当下标,表项里存的就是对应的物理页帧号。

虚拟页号 →  页表项
   0    →   页帧 5
   1    →   页帧 2
   2    →   页帧 9
   ...

  地址转换时,硬件拿虚拟地址里的页号去查这张表,得到页帧号,再拼上原来的页内偏移,就得到了物理地址。

  每个进程都有自己的一份页表。进程 A 和进程 B 可以拥有相同的虚拟页号,却指向不同的页帧——这正是“虚拟地址相同、物理地址不同”的实现方式

页表项不只有页帧号

  页表项里存的,其实不止“页帧号”。常见的还有:

  • 有效位:这一页当前是否在内存里
  • 保护位:这一页可读、可写还是可执行
  • 访问位、脏位等:供置换和缓存管理使用(后面会讲)

  有效位尤其重要:如果某一页暂时不在内存里(被挪到了磁盘),有效位就是 0,访问它会触发一次“缺页”,由内核把它调回来。

一个绕不开的问题:页表太大

  页表好用,但很快会撞上规模问题。

  设想一个 32 位地址空间、页大小 4 KB:页内偏移占 12 位,剩下 20 位是页号,共有 (2^{20})(约一百万个)页。每个页表项哪怕只占几字节,整个页表也要几 MB。而且每个进程都要一份——几十上百个进程,光页表就吃掉大量内存。

  更糟的是,页表还要求连续存放、随时可查,这对内存布局也很不友好。

  显然,不能让页表这样膨胀下去。怎么在“查得动”和“存得下”之间找平衡?下一章先看单级页表下地址是怎么转换的,之后再引入“多级页表”来瘦身。

思考题

  页表为什么必须“每个进程一份”,而不能所有进程共用一张?如果共用,会发生什么?

小结

知识点

  • 页表记录虚拟页到物理页帧的映射
  • 每个进程一份页表,用来实现进程间隔离
  • 页表项含页帧号、有效位、权限位等
  • 页表规模可能很大,是新的难题

参考资料

  1. Wikipedia(zh):页表:page table
  2. Wikipedia(zh):虚拟内存:虚拟内存

思考题答案(仅供参考)

  因为页表承载的正是“这个进程的虚拟地址究竟映射到哪些物理页”这份私有信息。如果所有进程共用一张,那么同一个虚拟页号就只能对应同一个物理页帧,进程之间再也无法拥有各自独立的内存——A 改的数据 B 直接就能看到,隔离荡然无存。所以,页表必须每个进程一份,它既是地址转换的依据,也是进程隔离的基石。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪