页命中与缺页
我们在上篇文章什么是内存(一):存储器层次结构中说过缓存命中与不命中的问题,都是缓存思想,在这里肯定也会存在同样的问题。并且磁盘与主存之间的缓存不命中代价肯定大的多。因为L0-L4之间,每级缓存的速度大约相差10倍左右,但是L4主存与L5磁盘之间,它们的速度相差约十万倍。所以主存与磁盘之间交换的页容量是最大的,尽可能的增加命中率。相应的替换策略,操作系统也使用了更加复杂精密的算法。
在上篇文章什么是内存(一):存储器层次结构,每次替换的区域,我们用了块(block),而这里我们却在说页(page), 其实同一个意思。只是因为历史原因,叫法不同罢了。
当CPU想要读取包含在某个虚拟页的内容时,如果该页已经缓存在主存中,也就是页命中。perfect,很完美。但是如果该页没有缓存在主存中,则我们称之为缺页(page fault)

图15:对VP3中的字的应用会引起不命中
如上图所示,CPU 引用了 VP3 中的内容, VP3 并未缓存在主存中。系统从内存中读取 PTE3,得知 VP3 未被缓存,这会触发了一个缺页异常。缺页异常会调用kernel的缺页异常处理程序,该程序会选择一个牺牲页。如下图所示,牺牲页选择了存放在 PP3 中的 VP4。

图16:VP4被牺牲了
此时如果 VP4 的内容被修改了,kernel会将它复制回磁盘。接下来,kernel从磁盘赋值 VP3 到内存中的 PP3并更新 PTE3。随后返回用户进程。当异常处理程序返回时,它会重启执行导致缺页的指令,当重新执行这条指令时,因为 VP3 已经在主存中了,此时就是页命中了。

图17:VP3被缓存到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)。
段 和 页
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)