关于进程页表和页目录是存放在内核空间,还是用户空间,低端还是高端内存的思考和验证

首先,如果内核没有配置高端内存,那么进程页表肯定就是在低端内存了,也就是全部在内核空间了。
在配置了高端内存的情况下,进程页表的pgd,pud,pmd,pte这些,应该放在内核空间的低端内存,还是高端内存,内核空间还是用户空间?由于内核通过cr3能获得全局页目录中的物理地址,由于低端内存的线性映射,内核就能据此算出页目录的虚拟地址,进而实现对页目录的读写,但是,如果所有进程的页表都存放在低端内存,感觉进程一多,低端内存会着不住。而如果页表在用户空间(或在内核空间的非线性映射区),则会因为内核不知道页表的虚拟地址而无法完成对页目录的读写。
接下来来查看代码(4.2内核版本),从x86的__handle_mm_fault入手,里面会对缺页异常进行处理(如分配页目录,页表等):

3314         pud = pud_alloc(mm, pgd, address);

函数执行流:

pud_alloc-->__pud_alloc-->pud_alloc_one

126 static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
127 {
128         return (pud_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT);
129 }

get_zeroed_page-->__get_free_pages

3251 unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
3252 {
3253         struct page *page;
3254 
3255         /*
3256          * __get_free_pages() returns a 32-bit address, which cannot represent
3257          * a highmem page
3258          */
3259         VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);
3260 
3261         page = alloc_pages(gfp_mask, order);
3262         if (!page)
3263                 return 0;
3264         return (unsigned long) page_address(page);
3265 }

关键是

3259    VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);

即限定不能在高端内存申请页,所以pud用的是低端内存。
不过还有不同的清况,看pte分配:

__pte_alloc --> pte_alloc_one -->alloc_pages

即pte使用的页可以是高端内存(没有限制页不能来自高端内存),和想象的有点不一样,不过仔细想想也是,因为到页表项pte这一步,页表已经建立完成了。继续看一下代码:

69 static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
 70                                 struct page *pte)
 71 {
 72         unsigned long pfn = page_to_pfn(pte);
 73 
 74         paravirt_alloc_pte(mm, pfn);
 75         set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE));
 76 }

该函数的功能是把pte的物理地址写到pmd上面。首先通过page_to_pfn确定struct page *pte对应的页帧号,然后通过pfn << PAGE_SHIFT确定该页帧号对应的物理地址,然后写入pmd。

你可能感兴趣的:(linux,kernel,页表,高端内存)