fork() 中的 copy-on-write 如何处理多个 fork?

ssg*_*gao 29 linux c fork

根据维基百科(这可能是错误的)

当发出 fork() 系统调用时,会创建与父进程对应的所有页面的副本,并由操作系统为子进程加载到单独的内存位置。但这在某些情况下是不需要的。考虑以下情况:子进程执行“ exec”系统调用(用于从 C 程序中执行任何可执行文件)或在fork(). 当子进程只需要为父进程执行命令时,不需要复制父进程的页面,因为exec用要执行的命令替换调用它的进程的地址空间。

在这种情况下,会使用一种称为写时复制 (COW) 的技术。使用这种技术,当发生 fork 时,不会为子进程复制父进程的页面。相反,页面在子进程和父进程之间共享。每当进程(父进程或子进程)修改页面时,都会为执行修改的进程(父进程或子进程)单独制作该特定页面的副本。然后,此过程将使用新复制的页面,而不是在所有未来引用中共享的页面。另一个进程(没有修改共享页面的进程)继续使用页面的原始副本(现在不再共享)。这种技术称为写时复制,因为当某个进程写入页面时,页面会被复制。

似乎当其中一个进程尝试写入页面时,页面的新副本会被分配并分配给生成页面错误的进程。之后原始页面被标记为可写。

我的问题是:如果fork()在任何进程尝试写入共享页面之前多次调用会发生什么?

jll*_*gre 25

没有什么特别的事情发生。所有进程都共享同一组页面,并且每个进程在想要修改页面时都有自己的私有副本。

  • 子进程并没有那么特别。在 fork 之后,子进程和父进程都具有相同的只读页面集。就这些页面而言,页面处理是对称的。 (11认同)

小智 6

fork() 的行为取决于 *nix 系统是否有 MMU。在非 MMU 系统(如早期的 PDP-11)上,fork() 系统调用复制了每个孩子的所有父级内存。在基于 MMU 的 *nix 系统上,内核将所有非堆栈页面标记为 R/O,并在父子进程之间共享它们。然后,当任一进程写入任何页面时,MMU 会捕获该尝试,然后内核分配一个可写页面并更新 MMU 页表以指向现在可写的页面。这种写时复制行为提供了加速,因为最初只需要为每个子进程分配和克隆一个私有堆栈。

如果您在每次 fork() 调用之间执行一些父代码,那么生成的子进程将因父进程更改的页面而异。另一方面,如果父进程简单地发出多个 fork() 调用,例如在一个循环中,那么子进程将几乎相同。如果使用局部循环变量,那么在每个子堆栈中将有所不同。