【问题标题】:How does fork() process mark parent's PTE's as read only?fork() 进程如何将父级的 PTE 标记为只读?
【发布时间】:2020-02-23 15:44:04
【问题描述】:

我搜索了很多资源,但没有发现任何具体的问题:

我知道在某些 linux 系统中,fork() 系统调用适用于写时复制;也就是父子共享同一个地址空间,但是PTE现在被标记为read-only,供COW以后使用。当任一尝试访问页面时,会出现PAGE_FAULT,并且该页面被复制到另一个可以修改的地方。

但是,我无法理解操作系统如何到达共享 PTE 以将它们标记为“已读”。我假设当 fork() 系统调用发生时,操作系统会在父页面表上执行“页面遍历”并将它们标记为 只读 - 但我没有找到对此的确认,或者任何有关流程的信息。

有人知道这些页面是如何被标记为只读的吗?将不胜感激任何帮助。谢谢!

【问题讨论】:

  • Linus 的操作系统“Linux”确实在 fork 系统调用实现中的 VMA 上运行:do_fork -> copy_process -> copy_mm -> dup_mm -> dup_mmap ... 我在这里无法获得确切的行,因此 Internet 搜索机提示“fork+COW+dup_mm”为 gist.github.com/cwshu/7d52bc993525c1bb7df1 - 所以真正的工作在 retval = copy_page_range(mm, oldmm, mpnt); 行 - 检查 mm/memory.c#L1005

标签: linux-kernel fork virtual-memory copy-on-write page-tables


【解决方案1】:

Linux 操作系统通过迭代父进程的所有内存范围(mmaps、堆栈和堆)来实现系统调用 fork。该范围的复制(VMA - Virtual memory areas 在函数 copy_page_range (mn/memory.c) 中具有循环页表条目:

    /*
     * 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);
    }

其中is_cow_mapping 将适用于私有和潜在可写页面(位域标志检查共享和maywrite bits 并且应该只设置maywrite 位)

#define VM_SHARED   0x00000008
#define VM_MAYWRITE 0x00000020

static inline bool is_cow_mapping(vm_flags_t flags)
{
    return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
}

https://www.kernel.org/doc/gorman/html/understand/understand006.html 等书籍和LWN 2005: "Four-level page tables merged" 等文章中描述了PUD、PMD 和PTE。

fork 实现如何调用copy_page_range:

  • fork 系统调用实现(sys_fork?syscall_define0(fork))是 do_fork (kernel/fork.c),它将调用
  • copy_process 将调用many copy_* functions,包括
  • copy_mm 调用
  • dup_mm 分配和填充新的 mm 结构,其中大部分工作由
  • 完成
  • dup_mmap(仍然是 kernel/fork.c)将检查映射的内容和方式。 (这里我无法获得 COW 实现的确切路径,所以我使用 Internet 搜索机和类似“fork+COW+dup_mm”的东西来获得类似 [1][2][3] 的提示)。检查 mmap 类型后,有 retval = copy_page_range(mm, oldmm, mpnt); 行来做实际工作。

【讨论】:

  • 很好的侦探工作,虽然你的链接很旧。在最新的内核版本中,会发生copy_page_range()copy_p4d_range()copy_pud_range()copy_pmd_range()copy_pte_range()copy_one_pte()。你错过了来自copy_page_range() 的中间call to copy_p4d_range(),它不再直接调用copy_pud_range()
  • 非常感谢你们!清楚地解释了一切。最后一个问题,为了澄清:迭代从哪里开始?提到迭代通过copy_page_range func 开始。查看附加的代码,我发现它也适用于其他数据结构(例如 TLB)。页表本身的迭代是否从 PTBR 开始(因此,迭代充当“页面遍历”)?我从代码中注意到的另一件事 - 上面的函数似乎在 PTE 上标记为“活动”。确定页面是活动还是非活动的函数是否相同?
  • @itaz 已在答案中解释,是 dup_mmap() 完成这项工作并决定致电 copy_page_range()
  • @itaz,迭代从 dup_mm -> dup_mmap 开始,它不是对基于硬件的页表进行迭代,而是对描述进程内存区域的 Linux 内核的软件结构进行迭代。所以它不是page walk,而是mmap walk。 dup_mmap iterates over mmap-ed regions 其中一些 mmap 可以共享或基于文件,而其他则是私有的。由于 COW 进程,一些页面已经可以被禁用写入。不确定您在哪里找到活动的,copy_one_pte 有非当前页面的分支(!pte_present(pte))(例如已交换)。
猜你喜欢
  • 1970-01-01
  • 2016-11-09
  • 1970-01-01
  • 1970-01-01
  • 2017-03-18
  • 1970-01-01
  • 2013-10-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多