Chinaunix首页 | 论坛 | 博客
  • 博客访问: 47512
  • 博文数量: 34
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-18 21:23
文章分类

全部博文(34)

文章存档

2014年(34)

我的朋友

分类: LINUX

2014-08-18 21:31:03

page fault不能发生在内核态么?为什么?
这里有3个问题:
1、为什么会产生page fault?
2、发生缺页的上下文是否可以位于内核态
3、发生缺页的地址是否可以位于内核态地址空间
问题1:为什么会产生page fault?
page fault是硬件提供的特性,本质为一种“异常”(应该了解“异常”和“中断”的概念吧~),实际由硬件直接触发,触发的条件为:CPU访问某线性地址时,如果没有找到其对应的页表项,则由硬件直接触发page fault。
因此,page fault产生的根源在于线性地址没有对应的页表项,即线性地址还没有分配实际的物理内存,没有进行映射。
------------
问题2:发生缺页的上下文位于内核态,是否可能?是否正常?
答:有可能在内核态,但这种情况下,发生缺页的地址只能位于用户态地址空间,而且也只能为exceptions table中预先定义好的异常,如果exceptions table中没有预先定义的处理,或者缺页的地址位于内核态地址空间,则表示错误,进入oops流程。通常属于异常,会导致oops。
相关代码如下:

点击(此处)折叠或打开

  1. static void __kprobes
  2. __do_page_fault(struct pt_regs *regs, unsigned long error_code)
  3. {
  4. ...
  5. if ((error_code & PF_USER) == 0 &&
  6.             !search_exception_tables(regs->ip)) {
  7.             bad_area_nosemaphore(regs, error_code, address);
  8.             return;
  9. ...

  10. }
bad_area_nosemaphore()处理流程:
  __bad_area_nosemaphore
    no_context
      oops_begin
        __die
------------
问题3:发生缺页的地址位于内核态地址空间是否可能?是否正常?
答:有可能,发生缺页的内核态地址仅可能位于vmalloc区。对内核来说(以32位为例),线性映射(直接通过TASK_SIZE偏移映射)的内存(对32位系统来说,就是前896M,即Zone_Normal)相应的页表在内核初始化时就已经建立,所以这部分内存对应的虚拟地址不可能产生page fault。
那么Vmalloc区为什么会产生page fault呢?内核使用Vmalloc分配内存时,不是已经分配了相应的物理内存,并创建了相应的页表项进行了映射吗?
答:的确,在使用Vmalloc分配物理内存时,确实进行了映射,创建了页表项。但是,其修改的仅为内核的主内核页表,并没有更新相关进程的页表。在Vmalloc分配内存后,第一次访问相关的线性地址时,由于相关进程的页表中并没有Vmalloc分配内存相应的页表项,所以会触发page fault,而这个page fault的处理过程其实就是:从内核主页表中同步相应的页表项到进程的页表中,完成这次同步后,进程中相关的页表项建立,后续再次访问相关线性地址时,就不会再产生缺页异常了。Vmalloc区的page fault处理相关代码如下:

点击(此处)折叠或打开

  1. /*
  2.      * 缺页地址位于内核空间。并不代表异常发生于内核空间,有可能是用户
  3.      * 态访问了内核空间的地址。
  4.      */
  5.     if (unlikely(fault_in_kernel_space(address))) {
  6.         if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {
  7.             //检查发生缺页的地址是否在vmalloc区,是则进行相应的处理
  8.             if (vmalloc_fault(address) >= 0)
  9.                 return;

点击(此处)折叠或打开

  1. /*
  2.   * 对于发生缺页异常的指针位于vmalloc区情况的处理,主要是将
  3.   * 主内核页表向当前进程的内核页表同步。
  4.   */
  5. static noinline __kprobes int vmalloc_fault(unsigned long address)
  6. {
  7.     unsigned long pgd_paddr;
  8.     pmd_t *pmd_k;
  9.     pte_t *pte_k;

  10.     /* Make sure we are in vmalloc area: */
  11.     /* 区域检查 */
  12.     if (!(address >= VMALLOC_START && address < VMALLOC_END))
  13.         return -1;

  14.     WARN_ON_ONCE(in_nmi());

  15.     /*
  16.      * Synchronize this task's top level page-table
  17.      * with the 'reference' page table.
  18.      *
  19.      * Do _not_ use "current" here. We might be inside
  20.      * an interrupt in the middle of a task switch..
  21.      */
  22.      /*获取pgd(最顶级页目录)地址,直接从CR3寄存器中读取。
  23.      *不要通过current获取,因为缺页异常可能在上下文切换的过程中发生,
  24.      *此时如果通过current获取,则可能会出问题*/
  25.     pgd_paddr = read_cr3();
  26.     //从主内核页表中,同步vmalloc区发生缺页异常地址对应的页表
  27.     pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);
  28.     if (!pmd_k)
  29.         return -1;
  30.     //如果同步后,相应的PTE还不存在,则说明该地址有问题了
  31.     pte_k = pte_offset_kernel(pmd_k, address);
  32.     if (!pte_present(*pte_k))
  33.         return -1;

  34.     return 0;
  35. }
阅读(1126) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~