页命中与缺页

📅 2026/7/2 4:37:01
页命中与缺页
我们在上篇文章什么是内存一存储器层次结构中说过缓存命中与不命中的问题都是缓存思想在这里肯定也会存在同样的问题。并且磁盘与主存之间的缓存不命中代价肯定大的多。因为L0-L4之间每级缓存的速度大约相差10倍左右但是L4主存与L5磁盘之间它们的速度相差约十万倍。所以主存与磁盘之间交换的页容量是最大的尽可能的增加命中率。相应的替换策略操作系统也使用了更加复杂精密的算法。在上篇文章什么是内存一存储器层次结构每次替换的区域我们用了块(block),而这里我们却在说页(page) 其实同一个意思。只是因为历史原因叫法不同罢了。当CPU想要读取包含在某个虚拟页的内容时如果该页已经缓存在主存中也就是页命中。perfect,很完美。但是如果该页没有缓存在主存中则我们称之为缺页(page fault)图15对VP3中的字的应用会引起不命中如上图所示CPU 引用了 VP3 中的内容 VP3 并未缓存在主存中。系统从内存中读取 PTE3,得知 VP3 未被缓存这会触发了一个缺页异常。缺页异常会调用kernel的缺页异常处理程序该程序会选择一个牺牲页。如下图所示牺牲页选择了存放在 PP3 中的 VP4。图16VP4被牺牲了此时如果 VP4 的内容被修改了kernel会将它复制回磁盘。接下来kernel从磁盘赋值 VP3 到内存中的 PP3并更新 PTE3。随后返回用户进程。当异常处理程序返回时它会重启执行导致缺页的指令当重新执行这条指令时因为 VP3 已经在主存中了此时就是页命中了。图17VP3被缓存到PP3根据习惯性的叫法我们在磁盘和内存之间传送页的活动叫做交换(swapping)或者页面调度(paging)。这种交换活动只有当不命中发生时才会发生(也就说系统并不会将磁盘内容预存到内存中)。这种策略被称之为按需页面调度(demand paging)。我们刚才说缺页错误是一种异常但是实际上在计算机系统中被0除读写文件还有上篇文章中我们所说的中断(interrupt)甚至包括我们代码中写的try catch都是一种异常。 比如被0除是intel 的CPU规定的的第0号故障(fault)类型的异常。而读写文件分别是linux规定的第0号和第1号陷阱(trap)类型的异常。多任务的上下文切换进程的创建回收等等与系统中这种异常流的处理密切相关。当然这是另外一个话题了。我们在这里不做累述。虚拟内存作为内存管理和内存保护的工具理所当然的每个进程都有一个独立的页表和一个独立的虚拟地址空间回到文章开头的问题比如每个C程序都要调用的stdio这个库不可能为每个进程都添加一份库内存中只有一份stdio库的内容供每个使用该库的进程共享。图18共享页面如上图所示: 第一个进程的的页表将 VP2 映射到 某个物理页面。而第二个进程同样将它的 VP2 映射到 该物理页面。所以该物理页面都被两个进程共享了。此时大家再看一下图:12 进程地址空间,就会发现在地址空间当中共享库的内存映射区域对于每个进程起始地址都是相同的。再想想进程之间共享内存的通信方式, 所以说虚拟内存简化了共享机制大家知道C语言中存在指针可以直接进行内存操作。因为有了虚拟内存所以我们的指针操作也不会访问到其他进程的区域但是哪怕是对于自己的地址空间很多内存区域也应该是禁止访问的这不仅包括kernel的区域也包括自己的只读代码段。那么虚拟内存就提供了这样的一种内存保护工具。地址翻译机制可以使用一种自然的方式来提供内存的访问控制。PTE 上添加一些额外的控制位来添加权限。每次 CPU 生成一个地址时地址翻译硬件都会读一个 PTE 。图19虚拟内存提供内存保护在上图中每个 PTE 额外添加了三个控制位 SUP 位表示进程是否必须运行内核模式READ和WRITE位分别控制页面的读写权限。如果有指令违反了这些控制权限那么 CPU 会触发一个故障并将控制传递给内核中的异常处理程序。该种异常一般称为段错误(segmentation fault)。段 和 页