vfork
的意图是,如果你只想在子进程中执行exec*
,则消除复制整个进程映像的开销。因为exec*
替换了子进程的整个映像,所以没有必要复制父进程的映像。
if ((pid = vfork()) == 0) {
execl(..., NULL); /* after a successful execl the parent should be resumed */
_exit(127); /* terminate the child in case execl fails */
}
对于其他类型的用途,vfork
是危险且不可预测的。
然而,在大多数当前的内核中,包括 Linux 在内,vfork
的主要优点已经消失,因为 fork
的实现方式。执行 fork
时,不是复制整个进程映像,而是使用写时复制技术。
vfork(2)
。一般来说,我猜每个被移植到非MMU平台上的*nix都有一个真正的vfork(2)
。 - ninjaljvfork
的说明页面清楚地阐明了它们之间的区别。这个主题对fork
、vfork
、clone
和exec
进行了很好的描述。fork
和vfork
之间的差异,我在一些Linux 2.6.3x嵌入式系统上遇到过这些差异。fork
将会失败。例如,如果父进程使用了2 GB的驻留内存(即已使用而不仅仅是分配的内存),如果剩余的空闲内存少于2 GB,则fork
将失败。当你只想执行一个简单的程序并且永远不需要那么大的父进程地址空间时,这是非常令人沮丧的!
vfork
没有这个内存问题,因为它不会复制父地址空间。子进程更像是一个线程,在其中可以调用exec*
或_exit
而不会损坏父进程。vfork
比fork
快得多,并且vfork
的执行时间不受父进程使用的内存量的影响,正如这里指出的那样:http://blog.famzah.net/2009/11/20/fork-gets-slower-as-parent-process-use-more-memory/。在性能临界或内存有限的情况下,vfork
+ exec*
可以成为 fork
+ exec*
的一个良好替代方案。问题在于其安全性较低,而且 man 手册指出 vfork
很可能会在将来被弃用。posix_spawn
函数,它是更高级别的,并提供了更多选项。它在可能的情况下安全使用 vfork
,这取决于您传递给它的选项。我已成功地使用 posix_spawn
并克服了 fork
+ exec
给我的那个烦人的“双重内存检查问题”。
关于此主题的一篇非常好的页面,其中包含一些 posix_spawn
示例链接。Runtime.exec()
传统上使用了 fork()/exec()
,但据我所知,Java 1.7 将在可用的情况下使用 vfork()/exec()
或 posix_spawn()
。 - ninjalj以下是我的 man 手册中的内容:
(来自 POSIX.1) vfork() 函数与 fork(2) 函数具有相同的效果,除了如果由 vfork() 创建的进程修改 pid_t 类型变量以外的任何数据,或者从调用 vfork() 的函数返回,或在成功调用 _exit(2) 或 exec(3) 系列函数之前调用任何其他函数,则行为是未定义的。
vfork() 与 fork(2) 不同之处在于父进程被挂起,直到子进程终止(正常地通过调用 _exit(2) 或异常地通过传递致命信号),或者它调用 execve(2)。在此之前,子进程与父进程共享所有内存,包括堆栈。子进程不能从当前函数返回或调用 exit(3),但可以调用 _exit(2)。
vfork()
大多是一个历史遗物 - 在具有合理写时复制语义的现代系统上,vfork()
通常不会带来太多好处(有时甚至本质上是 fork()
的别名!)。 - Nicholas Knight一些系统有一个名为 vfork() 的系统调用,最初是作为 fork() 的低开销版本设计的。由于 fork() 涉及将整个进程的地址空间复制一遍,因此非常昂贵,因此引入了 vfork() 函数(在 3.0BSD 中)。
然而,自从引入 vfork() 以来,fork() 的实现已经得到了显着改善,尤其是引入了“写时复制”机制,这种机制通过允许两个进程引用同一物理内存,直到它们中的任何一个进程修改该内存,就可以透明地模拟进程地址空间的复制。这主要消除了 vfork() 的正当理由;实际上,很大一部分系统现在完全缺乏原始的 vfork() 功能。不过,出于兼容性考虑,可能仍然存在一个 vfork() 调用,它只是简单地调用 fork() 而不试图模拟所有 vfork() 的语义。
因此,实际上利用 fork() 和 vfork() 之间的任何差异都是非常不明智的。事实上,除非您确切知道自己想要什么,否则使用 vfork() 也可能是不明智的。
两者之间的基本区别在于,使用 vfork() 创建新进程时,父进程会被暂时挂起,而子进程可能会借用父进程的地址空间。这种奇怪的状态会一直持续到子进程退出或调用 execve(),此时父进程恢复。
这意味着 vfork() 的子进程必须小心避免意外修改父进程的变量。特别是,子进程不能从包含 vfork() 调用的函数中返回,也不能调用 exit()(如果需要退出,应该使用 _exit();事实上,这对于普通 fork() 的子进程也是如此)。
vfork()
创建一个新进程时,父进程会被暂时挂起,而子进程可能会借用父进程的地址空间。这种奇怪的状态会一直持续,直到子进程退出或调用execve()
,此时父进程才会继续执行。vfork()
的子进程必须小心,避免意外修改父进程的变量。特别是,子进程不能从包含vfork()
调用的函数中返回,并且不能调用exit()
(如果需要退出,应该使用_exit();
实际上,对于普通fork()
的子进程也是如此)。vfork()
以来,fork()
的实现已经得到了极大的改进,其中最显著的是引入了“写时复制”技术,即通过允许两个进程引用相同的物理内存,直到它们中的任意一个修改该内存,从而透明地伪造了进程地址空间的复制。这在很大程度上消除了使用 vfork()
的正当理由;事实上,大部分系统现在完全缺乏 vfork()
的原始功能。但为了兼容性,可能仍然存在一个 vfork()
调用,它只是简单地调用 fork()
而不尝试模拟所有的 vfork()
语义。fork()
和 vfork()
之间的任何差异都是非常不明智的。事实上,除非你确切知道为什么要使用它,否则使用 vfork()
可能是不明智的。