linux-kernel - fork() 进程如何将父级的 PTE 标记为只读?

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

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

我知道对于某些 linux 系统,fork()系统调用与写时复制一起工作;即父子共享同一个地址空间,但PTE现在标记为只读 , 稍后在 COW 中使用。当任一尝试访问页面时,PAGE_FAULT发生并且页面被复制到另一个可以修改的地方。

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

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

最佳答案

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

  • copy_page_range iterate over pgd并调用
  • copy_pud_range 遍历 pud 并调用
  • copy_pmd_range 遍历 pmd 并调用
  • copy_pte_range 遍历 pte 并调用
  • copy_one_pte 它执行内存使用统计 (RSS) 并有几个代码段来处理 COW 案例:

  •     /*
         * 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对于私有(private)页面和潜在可写页面为真(检查位域标志是否为共享和 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;
    }
    

    PUD、PMD 和 PTE 在诸如 https://www.kernel.org/doc/gorman/html/understand/understand006.html 之类的书中有所描述。以及像 LWN 2005: "Four-level page tables merged" 这样的文章.

    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 Search Machine 和类似“fork+COW+dup_mm”的东西来获得类似 [1][2][3] 的提示)。检查 mmap 类型后有 retval = copy_page_range(mm, oldmm, mpnt);行做实际工作。
  • 关于linux-kernel - fork() 进程如何将父级的 PTE 标记为只读?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60363929/

    相关文章:

    c - c中使用fork()处理socket中多个客户端的逻辑问题

    c - 管道流和子进程

    memory-management - 进程虚拟地址空间和内核地址空间?如何?

    heap - 对象是堆中最小的可分页单元吗?

    linux - 任何 x86 处理器上是否有最小页面大小?

    c - 如何使用 linux 内核中的 lockdep 功能进行死锁检测

    linux - 用于 MPLS 配置的 iproute2 命令

    在进程上下文中调用 kmap_atomic()

    c - fork() 之后无法从子进程检索 mmap 共享内存

    android - 对 Android 5.0 的旧 Linux 内核支持