现在的位置: 首页 > 综合 > 正文

关于vmalloc缺页异常的一个问题

2016年12月04日 ⁄ 综合 ⁄ 共 1694字 ⁄ 字号 评论关闭

进程页表和vmalloc缺页异常

在新建进程时,新的线程会复制老的线程的页表,复制老线程的文件映射,匿名映射等,而且会复制页表项的全局页表项的内核部分,因为内核部分的页表的映射(除vmalloc)在启动内核时就已经建立好了,因此复制了全局页表项页就相当于复制了内核的页表。
pgd_t *pgd_alloc(struct mm_struct *mm)
{
pgd_t *pgd;
pmd_t *pmds[PREALLOCATED_PMDS];

pgd = (pgd_t *)__get_free_page(PGALLOC_GFP);

if (pgd == NULL)
goto out;

mm->pgd = pgd;

if (preallocate_pmds(pmds) != 0)
goto out_free_pgd;

if (paravirt_pgd_alloc(mm) != 0)
goto out_free_pmds;

/*
 * Make sure that pre-populating the pmds is atomic with
 * respect to anything walking the pgd_list, so that they
 * never see a partially populated pgd.
 */
spin_lock(&pgd_lock);

pgd_ctor(mm, pgd);
pgd_prepopulate_pmd(mm, pgd, pmds);

spin_unlock(&pgd_lock);

return pgd;

out_free_pmds:
free_pmds(pmds);
out_free_pgd:
free_page((unsigned long)pgd);
out:
return NULL;
}
static void pgd_ctor(struct mm_struct *mm, pgd_t *pgd)
{
/* If the pgd points to a shared pagetable level (either the
   ptes in non-PAE, or shared PMD in PAE), then just copy the
   references from swapper_pg_dir. */
if (PAGETABLE_LEVELS == 2 ||
    (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD) ||
    PAGETABLE_LEVELS == 4) {
clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY,
swapper_pg_dir + KERNEL_PGD_BOUNDARY,
KERNEL_PGD_PTRS);
}

/* list required to sync kernel mapping updates */
if (!SHARED_KERNEL_PMD) {
pgd_set_mm(pgd, mm);
pgd_list_add(pgd);
}
}
因此当进程在内核态运行时不需要切换页表,因此也不需要刷新tlb,高速缓存等。
vmalloc会产生缺页异常而持久映射和固定映射不会产生映射的原因。
主要原因就在于vmalloc映射的地址区域过大,不在一个PMD的范围内才会产生缺页异常。因为进程在内核态使用的本身的页表,而页表的内核部分的从TASK_SIZE到max_low_pn<<PAGE_SHIFT,持久映射和原子的持久映射的部分的PMD在内核启动时就确定了,因此内核的全局页表项中和这些PMD相关的部分已经固定好了不会改变的,因此只要进程copy了pgd就相当于复制了内核的页表。
由于持久映射一次映射一页,持久映射的大小LASK_PKMAP为1024或者512个页,而且PKMAP_BASE是PMD_MASK对齐的,在32位4K页下也就是4M内存对齐的,刚好在pgd中占一项,因此持久映射不会影响到内核的全局页表项,因此每个进程的pgd的内核部分都是有效的,每个进程都能看到新的映射,因此不会产生缺页异常,而固定映射的大小也不会超过1024,因此修改都是全局可见的,再说了固定的地址都保存在tlb不会被刷出的,因此固定映射页不会产生缺页异常。所以,若产生了缺页异常,且地址在内核部分,只有可能是在vmalloc区域。

抱歉!评论已关闭.