fork() 进程如何将父 PTE 标记为只读?

ITz*_*ITz 4 fork copy-on-write virtual-memory linux-kernel page-tables

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

我知道在某些 linux 系统中,fork()系统调用与 copy-on-write 一起使用;也就是说,父子进程共享相同的地址空间,但是 PTE 现在被标记为read-only,以便稍后在 COW 中使用。当任何一个尝试访问一个页面时,PAGE_FAULT都会发生一个页面,并将该页面复制到另一个可以修改的地方。

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

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

osg*_*sgx 6

Linux 操作系统通过迭代mmap父进程的所有内存范围(s、堆栈和堆)来实现系统调用 fork 。该范围的复制(VMA -虚拟内存区域在功能 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);
    }
Run Code Online (Sandbox Code Playgroud)

whereis_cow_mapping对私有页面和潜在可写页面为真(检查共享maywrite 位的位域标志,并且应该只设置 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;
}
Run Code Online (Sandbox Code Playgroud)

PUD、PMD 和 PTE 在https://www.kernel.org/doc/gorman/html/understand/understand006.html等书籍和LWN 2005:“四级页表合并”等文章中都有描述。

fork 实现如何调用copy_page_range

  • fork 系统调用实现(sys_fork?syscall_define0(fork))是do_fork(kernel/fork.c) 它将调用
  • copy_process这将调用许多 copy_* 函数,包括
  • copy_mm 其中调用
  • dup_mm 分配和填充新的 mm 结构,其中大部分工作由
  • dup_mmap(仍然是 kernel/fork.c),它将检查什么是 mmaped 以及如何。(在这里我无法获得 COW 实现的确切路径,所以我使用 Internet Search Machine 和类似“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()”调用“copy_p4d_range()”](https://elixir.bootlin.com/linux/latest/source/mm/memory.c#L1010),这并不直接不再调用“copy_pud_range()”。 (3认同)