当Linux系统中发生页面错误时,中断处理程序必须找出页面错误发生的原因。但如何呢?
- 有没有专门的号码!如果是,该号码记录在哪里?
- 在引发异常之前是否可以知道页面错误的原因?
例如。- 步骤1
通过CPU查找原因 - 第2步
引发异常
- 步骤1
答案1
当内存访问失败时,就会出现页错误,因为虚拟地址的 MMU 查找以无效描述符或指示缺乏权限的描述符结束(例如,尝试向只读页写入)。当发生页面错误时,处理器会执行一些操作;详细信息特定于每种处理器架构,但要点是相同的:
- 切换到特权模式(例如内核模式)。
- 设置一些寄存器以至少指示故障的性质以及故障点的程序计数器和处理器模式。
- 跳转到内存中的特定地址,由寄存器指示或本身在内存中的特定位置查找:页面错误处理程序的地址。
举个例子,在(32位)ARM处理器上:
- 这
dfsr
寄存器设置为描述故障的值(无论是由于读取或写入、处理器指令还是 DMA 等)。 - 这
dfar
寄存器设置为导致故障的访问目标的虚拟地址。 - 处理器切换到中止模式(内核级特权之一模式)。
- 在发生故障时,该
lr
寄存器被设置为程序计数器,并且在发生故障时,该spsr
寄存器被设置为程序状态寄存器(cpsr
包含模式位等的寄存器)。 sp
和寄存器cpsr
被存储:它们从中止模式下最后设置的值恢复。- 执行跳转到中止向量,其中之一异常向量。
页面错误处理程序的代码是操作系统内核的一部分。它的工作是分析故障原因并采取措施。它可以查阅提供有关故障性质信息的专用寄存器,如果需要,它还可以检查程序正在执行的指令。它还可以在MMU表中查找描述符;无效的描述符有时可以对信息进行编码,例如交换空间中页面的位置。内核通过查看全局变量的值或在每次上下文切换时更新的寄存器来了解当前正在执行哪个任务。以下是页面错误的一些常见行为:
- 有关进程内存映射的数据表明该页处于交换状态。内核找到一个空闲的物理页,或者通过删除包含磁盘缓存的页来获取一个物理页,或者通过首先将其内容保存到交换区来获取一个物理页。然后,它将数据从交换区加载到该物理页,并更改 MMU 表,以便导致故障的虚拟地址现在附加到进程的 MMU 映射中的该物理页。最后,内核安排切换回导致错误的指令处的进程;此时指令将成功执行。
- 有关进程内存映射的数据表明该页是写时复制页,并且已尝试进行写访问。与前面的情况类似,内核获取一个备用物理页,将数据复制到其中(此处是从只读页),更改 MMU 描述符,并安排进程再次执行指令。
- 有关进程内存映射的数据表明该页面未映射,或者没有必要的权限。在这种情况下,内核向进程传递 SIGSEGV 信号(分段错误):进程的执行在信号处理程序而不是原始位置恢复,但原始位置保存在堆栈上。如果进程没有 SIGSEGV 的处理程序,则会终止。
通常不可能确定异常即将发生,除非了解虚拟内存配置并在内存访问之前进行检查。正常的操作流程是,当页面错误发生时,处理器记录页面错误的原因。