Fork 创建了一个新进程,与它的 parent 完全一样

Fork creates a new process that is exactly the same as its parent

根据我问题标题中的假设"Fork create a new process that is exactly the same as its parent"。我想知道操作系统是如何真正创建一个fork的。

考虑一个繁重的进程(巨大的 RAM 占用空间),它会自行分叉以完成一项小任务(将文件列出到一个目录中)。根据假设,我预计 child 过程将与第一个过程一样大。然而我的常识告诉我,这不可能。

它在现实世界中如何运作?

当进程分叉时,子进程使用与其父进程相同的页面 table util 父进程或子进程写入其 space!So

正如其他人在评论中提到的,一种称为 Copy-On-Write 的技术减轻了复制 [=99= 的整个内存 space 的沉重成本] 过程。 Copy-on-write 意味着内存页面在 parent 和 child 之间共享 read-only 直到它们中的任何一个决定写入——此时页面被复制并且每个进程都有自己的私人副本。这种技术很容易防止大量复制,在很多情况下这会浪费时间,因为 child 将 exec() 或做一些简单的事情并退出。

详细情况如下:

当您调用 fork(2) 时,唯一的直接成本是分配新的唯一进程描述符的成本和复制 parent 的页表的成本。在 Linux 中,fork(2)clone(2) 系统调用实现,这是一个更通用的系统调用,允许调用者控制新进程的哪些部分与 parent 共享.当从 fork(2) 调用时,会传递一组标志以指示不会共享任何内容(您可以选择共享内存、文件描述符等 - 这就是线程的实现方式:通过调用 clone(2) CLONE_VM,表示 "share the memory space").

在幕后,每个进程的内存页面都有一个位标志,即 copy-on-write 标志,指示是否应在写入之前复制该页面。 fork(2) 用该位标记进程中的每个可写页面。每个页面还维护一个引用计数。

因此,当进程分叉时,内核会在该进程的每个 non-private 可写页上设置 copy-on-write 位并增加引用计数一个。 child 进程有指向这些相同页面的指针。

然后,每个页面都被标记为 read-only 以便尝试写入该页面会产生页面错误 - 这是唤醒内核所必需的,以便它有机会看到发生了什么以及发生了什么需要完成。

当任一进程写入仍在共享的页面并因此被标记为 read-only 时,内核将唤醒并尝试找出出现页面错误的原因。假设 parent / child 进程正在写入合法位置,内核最终会发现页面错误是由于页面标记为 copy-on-write 并且对该页面有多个引用。

然后内核分配内存,将页面复制到新位置,写入可以继续。

分叉有何不同

您说 fork(2) 创建了一个与其 parent 完全相同的新进程。这是不完全正确的。 parent 和 child 之间有几个区别:

  1. 进程ID不同
  2. parent 进程 ID 不同
  3. child的资源使用(CPU时间等)设置为0
  4. parent 拥有的文件锁不被继承
  5. child 上的待处理信号集已清除
  6. 已在 child
  7. 上清除未决警报

关于vfork

vfork(2) 系统调用与 fork() 非常相似,但它绝对不进行复制 - 它甚至不复制 parent 的页表。随着 copy-on-write 的引入,它不再被广泛使用,但历史上它被分叉后调用 exec() 的进程使用。

当然,在 vfork() 之后尝试在 child 进程中写入内存会导致混乱。