2017-02-24

1. 问题引入

摘自APUE:

由于在fork之后进程跟随着exec, 所以现在的很多实现并不执行一个父进程数据段, 栈和堆的完全复制. 作为替代, 使用了COW技术. 这些区域由父子进程共享, 而且内核将它们的访问权限改变为只读的. 如果父子进程中的任一个试图修改这些区域, 则内核只为修改区域的那块内存制作一个副本, 通常是虚拟存储系统中的一"页".

问题:哪里设置只读, 如何知道只读?

2. 解

快速tips:
PTE entry is marked as un-writeable.
But VMA is marked as writeable.

copyonepte()会调用 ptepsetwrprotect()

还是先从dup_mm()开始看吧.
linux-3.10.86/kernel/fork.c

struct mm_struct *dup_mm(struct task_struct *tsk)
{
    struct mm_struct *mm, *oldmm = current->mm;
    mm = allocate_mm();
    memcpy(mm, oldmm, sizeof(*mm)); 

    mm_init(mm, tsk)); //里面涉及页表
    err = dup_mmap(mm, oldmm);

}

static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
|-- mm_alloc_pgd(mm)
|   |--mm->pgd = pgd_alloc(mm); //分配页表

linux-3.10.86/arch/arm/mm/pgd.c

pgd_alloc
{
    new_pgd = __pgd_alloc();
    memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));  //清空页表

    /*
     * Copy over the kernel and IO PGD entries  拷贝内核的页表, 且是first level页表
     */
    init_pgd = pgd_offset_k(0);  //内核的页表
    memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD,
               (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); 

}

linux-3.10.86/kernel/fork.c

static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
{
    struct vm_area_struct *mpnt;
    //这个是进程的虚拟地址区域, 非内核的虚拟地址区域
    for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) { 
        copy_page_range(mm, oldmm, mpnt);
    }
}

这里就是我们要关注的部分了.

copy_page_range
|--copy_pud_range
|   |--copy_pmd_range
|   |   |--copy_pte_range
|   |   |   |--copy_one_pte

copy_one_pte
{
    /*
     * If it's a COW mapping, write protect it both
     * in the parent and the child
     */
    if (is_cow_mapping(vm_flags)) {
        ptep_set_wrprotect(src_mm, addr, src_pte);
        pte = pte_wrprotect(pte);
    }
}

本文地址: https://awakening-fong.github.io/posts/mm/do_fork_cow

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus