![当发生页面错误时,Unix 如何确定错误地址是否在交换空间中?](https://linux22.com/image/72157/%E5%BD%93%E5%8F%91%E7%94%9F%E9%A1%B5%E9%9D%A2%E9%94%99%E8%AF%AF%E6%97%B6%EF%BC%8CUnix%20%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9A%E9%94%99%E8%AF%AF%E5%9C%B0%E5%9D%80%E6%98%AF%E5%90%A6%E5%9C%A8%E4%BA%A4%E6%8D%A2%E7%A9%BA%E9%97%B4%E4%B8%AD%EF%BC%9F.png)
当任何进程的虚拟地址发生页面错误时,linux/unix 操作系统如何确定该页面(该虚拟地址的)是否先前存在于内存中被交换并换出到磁盘(即该页面当前存在于交换中)空间)或该页面之前从未加载到内存(即该页面不存在于交换空间中)?
答案1
首先我们需要看看硬件层面发生了什么。当发生页面错误时内存管理单元尝试取消引用虚拟地址并找到该地址的无效描述符。当发生这种情况时,处理器会执行陷阱:它切换到内核模式,跳转到预定义的地址(陷阱向量)并填充一些寄存器以指示发生了页面错误,并提供足够的信息以允许内核确定发生了什么。请求的地址是什么,什么指令导致了错误,以及在什么任务中。我在这里给出了总体思路,具体细节因处理器架构而异。
陷阱处理程序代码是内核的一部分,它检查寄存器和内存以确定故障的性质(可以针对不同类型的异常调用相同的处理程序)、请求的地址以及先前处于活动状态的进程。然后,它会查阅一些数据结构,这些数据结构指示应该在该地址的该进程中映射什么内容。这里,数据结构的性质在处理器架构之间以及 Unix 变体之间也有所不同。某些处理器架构在无效描述符中具有足够的位,MMU 不会解释这些位来存储交换扇区号,在这种情况下,内核不需要查阅任何其他数据结构。如果不是这种情况,内核将查阅附加到进程的数据结构并描述其内存映射。
相同的机制还可能决定其他一些事情,例如:
- 对于内存映射文件,需要从文件加载页面。
- 内存已映射,但操作未获授权,例如,尝试写入只读内存。在这种情况下,内核会安排将控制权切换到进程的 SIGSEGV 信号(如果有),否则进程将终止。
- 尝试写入标记为 CPU 只读但标记为写入时复制的页面。在这种情况下,内核会复制该页面。
如果内核设法在 RAM 中获取包含进程所需内容的页面,它将更新进程的内存映射以反映这一点。然后内核将控制权转移回进程,就像上下文切换一样;但它不是在进程执行的指令之后立即返回控制,而是在该指令之前返回控制,以便再次执行错误指令,这次成功。
答案2
操作系统中的低级页面错误处理程序(在 CPU 的陷阱表中列出)从 CPU 获取错误地址,并使用该错误地址检查进程地址空间描述表中的条目。该表包含段描述符列表,每个段描述符包含基地址和大小。如果该地址不在该列表中。操作系统发送 SIGSEGV(segmentati violati)。
如果可以找到该地址,则负责包含故障地址的地址范围的段表条目还保存指向相关段驱动程序的驱动程序函数的指针。
段驱动程序管理 VMEM 到后台内存的操作。如果地址与交换空间相关,则负责的驱动程序的名称是anon
。
有许多段驱动程序,例如每个文件系统都有一个段驱动程序,因为基本上所有文件系统数据 I/O 操作都是通过 mmap() 处理的。