do_fork和COW
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