用户态页表共享内核主页表的PUD表和PMD表,所以可以看到内核态访问进程页表的时候(内核地址部分),PMD和PUD都是直接使用
/*
* 这里在为pgd分配从slab中页面的时候会调用pgd_ctor对页面进行初始化
* 其中就会把swapper_pg_dir(init进程的内核主页表的内容)的内容复制到新的进程页表的内核页表部分中
*/
void pgd_ctor(void *pgd, kmem_cache_t *cache, unsigned long unused)
{
unsigned long flags;
if (PTRS_PER_PMD == 1)
spin_lock_irqsave(&pgd_lock, flags);
/*
* 把swapper_pg_dir(init进程的内核主页表的内容)的内容复制到新的进程页表的内核页表部分中,
* 通过复制直接让新的pgd执行了swapper_pg_dir中的PUD表和PMD表,复用了swapper_pg_dir中的PMD和PUD表
* 通过这里可以知道,用户态页表共享内核主页表的PUD表和PMD表,
* 所以可以看到内核态访问进程页表的时候(内核地址部分),PMD和PUD都是直接使用
*/
memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); /*★*/
/*启用PAE的情况,后面还要再分配用户空间的PMD*/
if (PTRS_PER_PMD > 1)
return;
pgd_list_add(pgd);
spin_unlock_irqrestore(&pgd_lock, flags);
/*清空用户态地址的PMD的pgd*/
memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));
}
pgd_cache = kmem_cache_create("pgd",
PTRS_PER_PGD*sizeof(pgd_t),
PTRS_PER_PGD*sizeof(pgd_t),
0,
pgd_ctor,
PTRS_PER_PMD == 1 ? pgd_dtor : NULL);
pgd_t *pgd_alloc(struct mm_struct *mm)
{
int i;
/*
* 分配一个pgd表项,这里已分配就是一整个页面,对于未启动PAE的情况相当于已经分配了PMD和PUD的空间
* 注意,在pgtable_cache_init中注册了初始化函数为pgd_ctor
* pgd_ctor会把swapper_pg_dir(init进程的内核主页表的内容)的内容复制到新的进程页表的内核页表部分中
*/
pgd_t *pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL);
......
return pgd;
out_oom:
for (i--; i >= 0; i--)
kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1));
kmem_cache_free(pgd_cache, pgd);
return NULL;
}
进程地址空间的创建
copy_process->copy_mm->mm_init->mm_alloc_pgd -> pgd_alloc
使用内核页表的举例的情况
fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
......
vmalloc_fault:
{
int index = pgd_index(address);
unsigned long pgd_paddr;
pgd_t *pgd, *pgd_k;
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k;
pte_t *pte_k;
asm("movl %%cr3,%0":"=r" (pgd_paddr));
pgd = index + (pgd_t *)__va(pgd_paddr);
/**
* 把主内核页全局目录的线性地址赋给pgd_k
*/
pgd_k = init_mm.pgd + index;
if (!pgd_present(*pgd_k))
goto no_context;
pud = pud_offset(pgd, address); //直接用,没有分配,因为共享了内核主页表的PUD
pud_k = pud_offset(pgd_k, address);
if (!pud_present(*pud_k))
goto no_context;
pmd = pmd_offset(pud, address); //直接用,没有分配,因为共享了内核主页表的PMD</span>
pmd_k = pmd_offset(pud_k, address);
if (!pmd_present(*pmd_k))
goto no_context;
set_pmd(pmd, *pmd_k); //这里有共享了PT,因为PMD,PUD都是共享的,所以vmalloc只可能是因为pgd中的present没有置位引发的
pte_k = pte_offset_kernel(pmd_k, address);
if (!pte_present(*pte_k))
goto no_context;
return;
}
}