首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

缺页异常详解(6)

缺页异常详解(6)

首先注意下个细节,在二级页表条目不存在时,会先创建条目;最终会调用函数handle_pte_fault,该函数功能注释已经描述很清楚,源码如下
static inline int handle_pte_fault(struct mm_struct *mm,
                   struct vm_area_struct *vma, unsigned long address,
                   pte_t *pte, pmd_t *pmd, unsigned int flags)
{
         pte_t entry;
         spinlock_t *ptl;
         entry = *pte;
/*调页请求:分为线性(匿名/文件)映射、非线性映射、swap情况下映射

注意,pte_present(entry)0说明二级页表条目pte映射的物理地址(*pte)不存在,很可能是调页请求*/

         if (!pte_present(entry)) {
        /*(pte_none(entry))1说明二级页表条目pte尚且没有写入任何物理地址,说明还根本从未分配物理页*/
                   if (pte_none(entry)) {
            /*如果该vma的操作函数集合实现了fault函数,说明是文件映射而不是匿名映射,将调用do_linear_fault分配物理页*/
                            if (vma->vm_ops) {
                                     if (likely(vma->vm_ops->fault))
                                               return do_linear_fault(mm, vma, address,
                                                        pte, pmd, flags, entry);
                            }
            /*匿名映射的情况分配物理页,最终调用alloc_pages*/
                            return do_anonymous_page(mm, vma, address,
                                                         pte, pmd, flags);
                   }
        /*(pte_file(entry))说明是非线性映射,调用do_nonlinear_fault分配物理页*/
                   if (pte_file(entry))
                            return do_nonlinear_fault(mm, vma, address,
                                               pte, pmd, flags, entry);
        /*如果页框事先被分配,但是此刻已经由主存换出到了外存,则调用do_swap_page()完成页框分配*/
                   return do_swap_page(mm, vma, address,
                                               pte, pmd, flags, entry);
         }
/*写时复制
    COW的场合就是访问映射的页不可写,有两种情况、:
一种是之前给vma映射的是零页(zero_pfn)

另外一种是访问fork得到的进程空间(子进程与父进程共享父进程的只读页)


共同特点就是: 二级页表条目不允许写,简单说就是该页不可写*/

         ptl = pte_lockptr(mm, pmd);
         spin_lock(ptl);
         if (unlikely(!pte_same(*pte, entry)))
                   goto unlock;
    /*是写操作时发生的缺页异常*/
         if (flags & FAULT_FLAG_WRITE) {
        /*二级页表条目不允许写,引发COW*/
                   if (!pte_write(entry))
                            return do_wp_page(mm, vma, address,
                                               pte, pmd, ptl, entry);
        /*标志本页已脏*/
                   entry = pte_mkdirty(entry);
         }
         entry = pte_mkyoung(entry);
         if (ptep_set_access_flags(vma, address, pte, entry, flags & FAULT_FLAG_WRITE)) {
                   update_mmu_cache(vma, address, entry);
         } else {
                   /*
                    * This is needed only for protection faults but the arch code
                    * is not yet telling us if this is a protection fault or not.
                    * This still avoids useless tlb flushes for .text page faults
                    * with threads.
                    */
                   if (flags & FAULT_FLAG_WRITE)
                            flush_tlb_page(vma, address);
         }
unlock:
         pte_unmap_unlock(pte, ptl);
         return 0;
}
回过头看下那四个异常的情况,上面的内容会比较好理解些,首先获取到二级页表条目值entry,对于写时复制的情况,它的异常addr的二级页表条目还是存在的(就是说起码存在标志L_PTE_PRESENT),只是说映射的物理页不可写,所以由(!pte_present(entry))可界定这是请求调页的情况
在请求调页情况下,如果这个二级页表条目的值为0,即什么都没有,那么说明这个地址所在的vma是完完全全没有做过映射物理页的操作,那么根据该vma是否存在vm_ops成员即操作函数,并且vm_ops存在fault成员,这说明是文件映射而非匿名映射,反之是匿名映射,分别调用函数do_linear_faultdo_anonymous_page

仍然在请求调页的情况下,如果二级页表条目的值含有L_PTE_FILE标志,说明这是个非线性文件映射,将调用函数do_nonlinear_fault分配物理页;其他情况视为物理页曾被分配过,但后来被linux交换出内存,将调用函数do_swap_page再分配物理页
文件线性/非线性映射和交换分区的映射除请求调页方面外,还涉及文件、交换分区的很多内容,为简化起见,下面仅以匿名映射为例描述用户空间缺页异常的实际处理,而事实上日常使用的malloc都是匿名映射;
匿名映射体现了linux为进程分配物理空间的基本态度,不到实在不行的时候不分配物理页,当使用malloc/mmap申请映射一段物理空间时,内核只是给该进程创建了段线性区vma,但并未映射物理页,然后如果试图去读这段申请的进程空间,由于未创建相应的二级页表映射条目,MMU会发出缺页异常,而这时内核依然只是把一个默认的零页zero_pfn(这是在初始化时创建的,前面的内存页表的文章描述过)vma映射过去,当应用程序又试图写这段申请的物理空间时,这就是实在不行的时候了,内核才会给vma映射物理页,源码如下
static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
                   unsigned long address, pte_t *page_table, pmd_t *pmd,
                   unsigned int flags)
{
         struct page *page;
         spinlock_t *ptl;
         pte_t entry;
继承事业,薪火相传
返回列表