arch/arm64/include/asm/pgtable-prot.h:93 #define PAGE_NONE__pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)94 #define PAGE_SHARED__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)95 #define PAGE_SHARED_EXEC__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE)96 #define PAGE_READONLY__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)97 #define PAGE_READONLY_EXEC__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)98 #define PAGE_EXECONLY__pgprot(_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)99100 #define __P000PAGE_NONE101 #define __P001PAGE_READONLY102 #define __P010PAGE_READONLY103 #define __P011PAGE_READONLY104 #define __P100PAGE_EXECONLY105 #define __P101PAGE_READONLY_EXEC106 #define __P110PAGE_READONLY_EXEC107 #define __P111PAGE_READONLY_EXEC108109 #define __S000PAGE_NONE110 #define __S001PAGE_READONLY111 #define __S010PAGE_SHARED112 #define __S011PAGE_SHARED113 #define __S100PAGE_EXECONLY114 #define __S101PAGE_READONLY_EXEC115 #define __S110PAGE_SHARED_EXEC116 #define __S111PAGE_SHARED_EXEC
可以发现对于私有的映射只有只读(PTE_RDONLY)没有可写属性(PTE_WRITE)105-107行 ,虽然之前设置的时候是设置了可写(VM_WRITE)!而对应共享映射则会有可写属性 。
而这个被设置的保护位组合最终会在缺页异常中被设置到页表中:上面说到的do_anonymous_page函数:
2908entry = pte_mkspecial(pfn_pte(my_zero_pfn(vmf->address),2909vma->vm_page_prot));
对于私有匿名映射的页,假设设置的vmflags为VMREAD|VMWRITE则对应的保护位组合为:P110即为PAGE_READONLY_EXEC=pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PT_ENG | PTE_PXN)不会设置为可写 。
所以就将其页表设置为了只读!!!
2922行 跳转到setpte去将设置好的页表项值填写到页表项中 。
当匿名页读之后再次去写时候会由于页表属性为只读导致COW缺页异常,详将COW相关文章,再此不在赘述 。下面用图说话:
文章插图
3.3 第一次写匿名页的情况接着do_anonymous_page函数继续往下分析:
【Linux内核虚拟内存管理之匿名映射缺页异常分析】
2876 static vm_fault_t do_anonymous_page(struct vm_fault *vmf)2877 {...29242925/* Allocate our own private page. */2926if (unlikely(anon_vma_prepare(vma)))2927goto oom;2928page = alloc_zeroed_user_highpage_movable(vma, vmf->address);2929if (!page)2930goto oom;29312932if (mem_cgroup_try_charge_delay(page, vma->vm_mm, GFP_KERNEL, &memcg,2933false))2934goto oom_free_page;29352936/*2937¦* The memory barrier inside __SetPageUptodate makes sure that2938¦* preceeding stores to the page contents become visible before2939¦* the set_pte_at() write.2940¦*/2941__SetPageUptodate(page);29422943entry = mk_pte(page, vma->vm_page_prot);2944if (vma->vm_flags & VM_WRITE)2945entry = pte_mkwrite(pte_mkdirty(entry)); 29462947vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, vmf->address,2948&vmf->ptl);2949if (!pte_none(*vmf->pte))2950goto release;29512952ret = check_stable_address_space(vma->vm_mm);2953if (ret)2954goto release;29552956/* Deliver the page fault to userland, check inside PT lock */2957if (userfaultfd_missing(vma)) {2958pte_unmap_unlock(vmf->pte, vmf->ptl);2959mem_cgroup_cancel_charge(page, memcg, false);2960put_page(page);2961return handle_userfault(vmf, VM_UFFD_MISSING);2962}29632964inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);2965page_add_new_anon_rmap(page, vma, vmf->address, false);2966mem_cgroup_commit_charge(page, memcg, false, false);2967lru_cache_add_active_or_unevictable(page, vma);2968 setpte:2969set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);29702971/* No need to invalidate - it was non-present before */2972update_mmu_cache(vma, vmf->address, vmf->pte);2973 unlock:2974pte_unmap_unlock(vmf->pte, vmf->ptl);2975return ret;2976 release:2977mem_cgroup_cancel_charge(page, memcg, false);2978put_page(page);2979goto unlock;2980 oom_free_page:2981put_page(page);2982 oom:2983return VM_FAULT_OOM;2984 }
当判断不是读操作导致的缺页的时候,则是写操作造成,处理写私有的匿名页情况,请记住这依然是第一次访问这个匿名页只不过是写访问而已 。2928 行会分配一个高端 可迁移的 被0填充的物理页 。2941 设置页中数据有效 2943 使用页帧号和vma的访问权限设置页表项值(注意:这个时候页表项属性依然为只读) 。
2944-2945行 如果vma可写,则设置页表项值为脏且*可写*(这个时候才设置为可写) 。
2964行 匿名页计数统计 2965行 添加到匿名页的反向映射中 2967行 添加到lru链表 2969 将设置好的页表项值填充到页表项中 。
推荐阅读
- 在Linux中使用Bashtop与Bpytop监管系统资源
- Linux驱动-互斥锁用法,建议先保存
- Linux,VMware桥接配置Centos网络
- 理解Linux下的SELinux
- 云主机数据库导入与导出
- Windows Server与Linux:究极对比
- Linux驱动基石之POLL机制
- Linux ALSA 图解
- Linux 下的交互式进程浏览器 htop 3.0.0 发布
- 带你玩转 Linux Shellcode