用于进程创建的 UNIX 系统调用 fork() 通过复制父进程来创建子进程。我的理解是,这几乎总是随后调用 exec() 来替换子进程的内存空间(包括文本段)。在 fork() 中复制父级的内存空间对我来说总是很浪费(尽管我意识到可以通过使内存段进行写时复制(因此只复制指针)来最大程度地减少浪费)。无论如何,有谁知道为什么流程创建需要这种重复方法?
答案1
是为了简化界面。fork
和的替代品exec
将类似于 Windows'创建进程功能。请注意有多少参数CreateProcess
,其中许多是具有更多参数的结构。这是因为一切您可能希望控制新进程必须传递给CreateProcess
.事实上,CreateProcess
没有足够的参数,所以微软不得不添加创建进程为用户和使用登录创建进程。
使用该fork/exec
模型,您不需要所有这些参数。相反,进程的某些属性被保留在exec
.这允许您fork
,然后更改您想要的任何流程属性(使用您通常使用的相同功能),并且然后 exec
。在Linux中,fork
没有参数,execve
只有3个:要运行的程序、给它的命令行以及它的环境。 (还有其他exec
函数,但它们只是execve
C 库提供的包装器,用于简化常见用例。)
如果您想使用不同的当前目录启动进程: fork
, chdir
, exec
.
如果您想重定向 stdin/stdout: fork
、关闭/打开文件、exec
.
如果要切换用户:fork
, setuid
, exec
。
所有这些东西都可以根据需要进行组合。如果有人提出了一种新的流程属性,您不必更改fork
和exec
。
正如 larsks 提到的,大多数现代 Unix 使用写时复制,因此fork
不会涉及大量开销。
答案2
除了 cjm 的答案之外,单一 Unix 规范还定义了一个名为 的函数vfork()
。该函数的工作方式与 fork 类似,不同之处在于,如果分叉进程除了尝试调用 exec 系列函数或调用_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
,并且提供了示例实现。)