科技大本营|Linux内核虚拟内存管理之匿名映射缺页异常分析( 二 )

3782和3783行是匿名映射缺页异常的触发条件:
1.发生缺页的地址所在页表项不存在 。
2.是匿名页发生的 , 即是vma->vm_ops为空 。
当满足这两个条件的时候就会调用do_anonymous_page函数来处理匿名映射缺页异常 。
2871 /*2872* We enter with non-exclusive mmap_sem (to exclude vma changes,2873* but allow concurrent faults), and pte mapped but not yet locked.2874* We return with mmap_sem still held, but pte unmapped and unlocked.2875*/2876 static vm_fault_t do_anonymous_page(struct vm_fault *vmf)2877 {2878struct vm_area_struct *vma = vmf->vma;2879struct mem_cgroup *memcg;2880struct page *page;2881vm_fault_t ret = 0;2882pte_t entry;28832884/* File mapping without ->vm_ops ? */2885if (vma->vm_flags28872888/*2889|* Use pte_alloc() instead of pte_alloc_map().We can't run2890|* pte_offset_map() on pmds where a huge pmd might be created2891|* from a different thread.2892|*2893|* pte_alloc_map() is safe to use under down_write(mmap_sem) or when2894|* parallel threads are excluded by other means.2895|*2896|* Here we only have down_read(mmap_sem).2897|*/2898if (pte_alloc(vma->vm_mm, vmf->pmd))2899return VM_FAULT_OOM;2904...2885行判断:发生缺页的vma是否为私有映射 , 这个函数处理的是私有的匿名映射 。
2898行 如何页表不存在则分配页表(有可能缺页地址的页表项所在的直接页表不存在) 。
3.2 第一次读匿名页情况...2905/* Use the zero-page for reads */2906if (!(vmf->flags2910vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd,2911vmf->address,2912if (!pte_none(*vmf->pte))2913goto unlock;2914ret = check_stable_address_space(vma->vm_mm);2915if (ret)2916goto unlock;2917/* Deliver the page fault to userland, check inside PT lock */2918if (userfaultfd_missing(vma)) {2919pte_unmap_unlock(vmf->pte, vmf->ptl);2920return handle_userfault(vmf, VM_UFFD_MISSING);2921}2922goto setpte;2923}...2968 setpte:2969set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);2906到2923行是处理的是私有匿名页读的情况:这里就会用到我们上面将的0页了 。
2906和 2907行判断是否是由于读操作导致的缺页而且没有禁止0页 。
2908-2909行是核心部分:设置页表项的值映射到0页 。
我们主要研究这个语句:pfn_pte用来将页帧号和页表属性拼接为页表项值:
arch/arm64/include/asm/pgtable.h:77 #define pfn_pte(pfn,prot)\78__pte(__phys_to_pte_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot))是将pfn左移PAGE_SHIFT位(一般为12bit) , 或上pgprot_val(prot)
先看my_zero_pfn:
include/asm-generic/pgtable.h:875 static inline unsigned long my_zero_pfn(unsigned long addr)876 {877extern unsigned long zero_pfn;878return zero_pfn;879 }mm/memory.c:126 unsigned long zero_pfn __read_mostly;127 EXPORT_SYMBOL(zero_pfn);128129 unsigned long highest_memmap_pfn __read_mostly;130131 /*132* CONFIG_MMU architectures set up ZERO_PAGE in their paging_init()133*/134 static int __init init_zero_pfn(void)135 {136zero_pfn = page_to_pfn(ZERO_PAGE(0));137return 0;138 }139 core_initcall(init_zero_pfn);||
\/
arch/arm64/include/asm/pgtable.h:54 /*55* ZERO_PAGE is a global shared page that is always zero: used56* for zero-mapped memory areas etc..57*/58 extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];59 #define ZERO_PAGE(vaddr)phys_to_page(__pa_symbol(empty_zero_page))


推荐阅读