用于进程创建的UNIX系统调用fork()通过复制父进程来创建子进程。我的理解是,几乎总是在此之后调用exec()来替换子进程的内存空间(包括文本段)。在fork()中复制父级的内存空间似乎一直对我很浪费(尽管我意识到可以通过使内存段在写入时进行复制来最小化浪费,从而仅复制指针)。无论如何,有人知道为什么流程创建需要这种重复方法吗?
select被破坏了,但这是另一回事了)。
用于进程创建的UNIX系统调用fork()通过复制父进程来创建子进程。我的理解是,几乎总是在此之后调用exec()来替换子进程的内存空间(包括文本段)。在fork()中复制父级的内存空间似乎一直对我很浪费(尽管我意识到可以通过使内存段在写入时进行复制来最小化浪费,从而仅复制指针)。无论如何,有人知道为什么流程创建需要这种重复方法吗?
select被破坏了,但这是另一回事了)。
Answers:
这是为了简化界面。对替代fork和exec将类似Windows的CreateProcess的功能。请注意有多少个参数CreateProcess,其中许多是带有更多参数的结构。这是因为一切你可能想控制对新工艺将被传递到CreateProcess。实际上,CreateProcess没有足够的参数,因此Microsoft必须添加CreateProcessAsUser和CreateProcessWithLogonW。
使用fork/exec模型,您不需要所有这些参数。取而代之的是,该过程的某些属性保留在之间exec。这允许您fork,然后换你想要的任何进程属性(使用相同的功能,你会正常使用),并随后 exec。在Linux中,fork没有参数,execve只有3:要运行的程序,提供它的命令行以及它的环境。(还有其他exec功能,但它们只是execveC库提供的包装器,以简化常见的用例。)
如果你想用不同的当前目录启动一个进程: fork,chdir,exec。
如果要重定向stdin / stdout:,请fork关闭/打开文件,exec。
如果你想切换用户:fork,setuid,exec。
所有这些东西都可以根据需要进行组合。如果有人提出了一种新的流程属性,则不必更改fork和exec。
就像larsk提到的那样,大多数现代的Unix使用写时复制,因此fork不会涉及大量开销。
除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,并提供示例实现。)
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风格就是这种情况。