内核缺页异常处理

在访问内核地址空间时,缺页异常可能被各种条件出发,如下所述:

  • 内核本身的程序设计错误导致访问不正确的地址,这个在稳定版本中永远不会发生,在开发版本中偶尔会发生
  • 内核通过用户空间传递的参数访问了无效地址
  • 访问使用vmalloc分配的区域,触发缺页异常

前两种情况是真正的错误,内核必须使用最后的手段---异常修正(exception fixup)机制来进行处理
vmalloc的情况是导致缺页异常的合理情况,必须加以校正。直至对应的缺页异常发生之前,vmalloc区域中的修改都不会传输到进程的页表中。因此在对vmalloc异常进行处理时,必须从主页表复制适当的访问权限信息到进程的页表中。

对于用户态发生的缺页异常,内核将使用按需调页机制,自动并透明地返回一个物理内存页;如果访问发生在内核态,则必须使用不同的手段进行校正。每次发生缺页异常时,将输出异常的原因和当前执行代码的地址。这使得内核可以编译一个列表,列出所有可能执行未授权内存访问操作处理(异常处理)的代码块。这就是"异常表"。

struct exception_table_entry {
     unsigned long insn, fixup;
};

insn: 指定了内核在虚拟地址空间发生异常的位置
fixup:指定了进行异常处理代码的地址

fixup_exception用于搜索异常表:

int fixup_exception(struct pt_regs *regs)
{
     const struct exception_table_entry *fixup;

#ifdef CONFIG_PNPBIOS
     if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
          extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
          extern u32 pnp_bios_is_utter_crap;
          pnp_bios_is_utter_crap = 1;
          printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
          __asm__ volatile(
               "movl %0, %%esp\n\t"
               "jmp *%1\n\t"
               : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
          panic("do_trap: can't hit this");
     }
#endif

     fixup = search_exception_tables(regs->ip);
     if (fixup) {
          regs->ip = fixup->fixup;
          return 1;
     }

     return 0;
}

regs->ip指向EIP寄存器,在IA32处理器上包含了出发异常的代码段地址。
当找到修正例程时,将指令指针设置到对应的内存位置。因此,在fixup_exception通过return返回时,内核将执行找到的异常处理程序。
如果没找到异常处理程序(修正例程),则表明出现了一个真正的内核异常,在对search_exception_tables调用不成功后,将跳转到do_page_fault中相应的异常标号处,最终将导致内核进入oops状态。

内核缺页异常处理_第1张图片
内核缺页异常处理_第2张图片

你可能感兴趣的:(内存管理)