用于进程创建的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
功能,但它们只是execve
C库提供的包装器,以简化常见的用例。)
如果你想用不同的当前目录启动一个进程: 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风格就是这种情况。