为什么默认流程创建机制为fork?


46

用于进程创建的UNIX系统调用fork()通过复制父进程来创建子进程。我的理解是,几乎总是在此之后调用exec()来替换子进程的内存空间(包括文本段)。在fork()中复制父级的内存空间似乎一直对我很浪费(尽管我意识到可以通过使内存段在写入时进行复制来最小化浪费,从而仅复制指针)。无论如何,有人知道为什么流程创建需要这种重复方法吗?


3
请注意,fork(2)Linux下的手册页说:Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child. 我想象(但不确定)其他现代Unix风格就是这种情况。
larsks 2012年

4
最初的PDP-11 Unix实际上确实复制了一个分叉进程的所有字节:但是它只有64Kb的可执行文件,最多只有64Kb的数据,所以即使在1975年,它也不是一个沉重的负担。猜想自1990年左右以来,每个unix和unix-a-like都有写时复制文本段,所以我什至不确定为什么书籍和文章会再传播“ fork的性能问题”。
Bruce Ediger'2

如今,fork的实现方式与vfork(openbsd.org/cgi-bin/…)类似。高效,不用担心。
Aki

还要注意,有很多用法,您在分叉后不执行(或至少不立即执行):考虑管道和Web服务器。
jfg956

您可能事情会很慢。但是就像@cjm所说的那样,看看Microsoft使用CreateProcess的替代方法,他们不得不尽早实现线程(这可能是他们导致的唯一事情),因为CreateProcess速度很慢。(他们还需要线程,因为它select被破坏了,但这是另一回事了)。
ctrl-alt-delor

Answers:


57

这是为了简化界面。对替代forkexec将类似Windows的CreateProcess的功能。请注意有多少个参数CreateProcess,其中许多是带有更多参数的结构。这是因为一切你可能想控制对新工艺将被传递到CreateProcess。实际上,CreateProcess没有足够的参数,因此Microsoft必须添加CreateProcessAsUserCreateProcessWithLogonW

使用fork/exec模型,您不需要所有这些参数。取而代之的是,该过程的某些属性保留在之间exec。这允许您fork,然后换你想要的任何进程属性(使用相同的功能,你会正常使用),并随后 exec。在Linux中,fork没有参数,execve只有3:要运行的程序,提供它的命令行以及它的环境。(还有其他exec功能,但它们只是execveC库提供的包装器,以简化常见的用例。)

如果你想用不同的当前目录启动一个进程: forkchdirexec

如果要重定向stdin / stdout:,请fork关闭/打开文件,exec

如果你想切换用户:forksetuidexec

所有这些东西都可以根据需要进行组合。如果有人提出了一种新的流程属性,则不必更改forkexec

就像larsk提到的那样,大多数现代的Unix使用写时复制,因此fork不会涉及大量开销。


16
很好的解释。“那些不了解UNIX的人注定要重造它,这很糟糕。” -亨利·斯宾塞
凯尔·琼斯

1
谢谢!您有参考吗?
Ellen Spertus'2

1
@Aki,不,CreateProcess()实际上是创建一个新进程并从头开始构建它,而不需要分叉。
psusi'2

2
但是在Unix某处是否一定没有等效的CreateProcess()吗?否则,如何创建第一个流程?与神话中的创造者神不同,第一个过程无法从虚无中叉出来。;-)
史蒂文

2
@StevenMonday,是的,但是它在内核的初始化代码中,不能从外部访问。它不需要所有这些参数,因为几乎所有内容都是硬编码的。它只能创建进程ID 1,也就是init进程。之后,仅通过分叉来创建进程。
cjm 2012年

5

除cjm的答案外,《单一Unix规范》还定义了一个名为的函数vfork()。该函数的工作方式与fork相似,不同之处在于,如果分叉的进程除了尝试调用exec familly函数或call以外,还具有未定义的行为_exit()

因此,定义行为的唯一用途是:

pid_t ret = vfork();
if(ret == 0)
{
    exec(...);
    _exit(EXIT_FAILURE); //in case exec failed for any reason.
}

那怎么vfork办?这是便宜的fork。在没有写时复制的实现中,生成的进程将与原始进程共享内存空间(因此具有未定义的行为)。在写时复制的实现中,vfork允许与相同fork(),因为写时复制的实现速度很快。

还有一个可选posix_spawn功能(和一个posix_spawnp功能),可以直接创建一个新进程。(也可以使用fork和通过库调用实现它们exec,并提供示例实现。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.