6 回答
的目的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
手册页清楚地说明了这些差异。本主题很好地描述了fork
、vfork
和。clone
exec
以下是我在使用过的一些 Linux 2.6.3x 嵌入式系统上遇到fork
的一些经常被忽视的差异。vfork
即使使用写时复制技术,fork
如果您没有足够的内存来复制父进程使用的内存,也会失败。例如,如果父进程使用 2 GB 的常驻内存(即,已使用的内存,而不是刚刚分配的内存),fork
如果剩余的可用内存少于 2 GB,则会失败。当您只想要exec
一个简单的程序并且因此永远不需要那么大的父地址空间时,这令人沮丧!
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*
。问题是它不太安全,并且手册页说vfork
将来可能会被弃用。
一个更安全、更便携的解决方案可能是看posix_spawn
功能,它级别更高,提供更多选择。它vfork
会尽可能安全地使用,具体取决于您传递的选项。我已经能够成功使用并克服+给我带来posix_spawn
的烦人的“双重内存检查问题” 。fork
exec
从我的手册页
(来自 POSIX.1)vfork() 函数与 fork(2) 具有相同的效果,除了如果 vfork() 创建的进程修改了用于存储从 vfork() 返回值,或从调用 vfork() 的函数返回,或在成功调用 _exit(2) 或 exec(3) 系列函数之一之前调用任何其他函数。
vfork() 与 fork(2) 的不同之处在于父进程被挂起,直到子进程终止(正常情况下,通过调用 _exit(2),或异常情况下,在传递致命信号后),或者它调用 execve(2 )。在此之前,子进程与其父进程共享所有内存,包括堆栈。子进程不能从当前函数返回或调用exit(3),但可以调用_exit(2)。
一些系统有一个系统调用 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()
,此时父进程继续。
这意味着a的子进程vfork()
必须小心避免意外修改父进程的变量。特别是,子进程不能从包含vfork()
调用的函数返回,也不能调用
exit()
(如果需要退出,应该_exit();
实际使用,对于普通的子进程也是如此fork()
)。
然而,自从vfork()
引入以来,实现有了很大的fork()
改进,最显着的是引入了“写时复制”,通过允许两个进程引用相同的物理内存,进程地址空间的复制被透明地伪造,直到他们中的任何一个都对其进行了修改。vfork();
这在很大程度上消除了事实上,很大一部分系统现在完全缺乏原始功能的
理由vfork()
。但是,为了兼容性,可能仍然存在一个vfork()
调用,它只是调用fork()
而不尝试模拟所有vfork()
语义。
因此,实际上利用 和 之间的任何差异是非常不明智fork()
的vfork()
。事实上,使用它可能是不明智的vfork()
,除非你确切地知道你为什么要使用。