不使用fork()创建子进程

11

是否有一种方法可以在不使用fork()的情况下,仅使用execvp()来启动子进程?


execvp会用另一个可执行文件替换掉您的进程,所以它并不是子进程。为什么会抵制fork呢? - Khouri Giordano
1
很遗憾,这个问题没有合理的解释,因为我的项目有特定的限制。 - gonidelis
1
请问您能否给我们提供项目的完整限制声明?也许您会从发现没有使用“fork”无法启动子进程中学到Unix的一些微妙之处,但也有可能是您误解了任务。 - zwol
4
你可能误解了作业要求,或者教授提出了不可能完成的要求。 - Eugene Sh.
什么是操作系统?有很多操作系统根本不使用fork()。 - user3344003
显示剩余5条评论
5个回答

8
你的问题的严谨答案是否定的。唯一创建新进程的系统调用是fork。execvp的底层系统调用(称为execve)将一个新的程序加载到现有进程中,这是不同的事情。
某些Unix物种除了fork之外还有其他系统调用(例如vfork、rfork、clone),它们只是fork本身的小变化,而且它们都不是POSIX标准的一部分,该标准指定了您可以在任何自称为Unix的东西上使用的功能。
稍微更有帮助的答案是你可能正在寻找posix_spawn,它是一个库例程,将fork和exec包装成单个操作,但我发现使用它正确比编写自己的fork和exec子例程更麻烦。你的经验可能不同。

@DanO 我认为这并不足以改变我的答案。当然,posix_spawn可能正在使用一个更新的fork-with-options原语(无论它叫什么),而不是原始的fork(2),但它仍然是一个库例程,它包装了一个fork操作和一个exec操作。它的算法没有改变。 - zwol
@DanO 请重新阅读答案的第二段:自从我最初写下它以来,就提到了克隆。由于克隆只是fork的一个小变体,它的存在并不否定我的原始观点,就像vfork一样。 - zwol
抱歉,我就是看不到。只要posix_spawn通过执行fork操作和exec操作来实现,我认为当前答案所说的是准确的。系统调用的名称并不重要。 - zwol
你坚持一个区别,据我所知,这并不对应于实际的差异。Clone执行fork操作。 - zwol
我会写自己的答案。 - Dan O

3
与 Windows 系统不同,在 Linux 和其他类 UNIX 系统中,创建一个新进程和执行一个新的进程镜像是两个不同的步骤。 fork 函数制作了一个调用进程的精确副本,并实际上返回两次,一次给父进程,一次给子进程。 execvp 函数(以及 exec 家族中的其他函数)在同一个进程中执行一个新的进程镜像,覆盖现有的进程镜像。
你可以直接调用 execvp 而无需先调用 fork。 如果这样做,那只意味着当前运行的程序消失,并被给定的程序替换。 然而,fork 是创建新进程的方法。

覆盖现有的进程镜像。进入实现细节,我想象它是懒惰地完成的,一开始只替换页面表与寄存器。 - Tony

3

posix_spawn 是不直接调用 fork 时创建子进程的唯一符合 posix 标准的方法。我说“不直接”是因为历史上 posix_spawn 本身会调用 forkvfork。然而,在 GNU/Linux 中已经不再是这样了。与直接使用 fork 相比,posix_spawn 本身可能更有效率,并且在代码试图运行不同可执行文件时也可能更符合概念。

如果您不担心可移植性,可以放弃 posix,直接与目标内核进行耦合。在 Linux 上,创建子进程的系统调用是 clone。在本回答编写时,手册页提供了三种变体的文档,包括相对较新的 clone3

我认为您可以从手册页中借鉴例子,并向 childFunc 添加一个 execvp 调用。不过我还没有尝试过!


1
然而,在GNU/Linux中情况已经不同了。posix_spawn本身可能比fork更有效率。原因是fork()在glibc中存在问题,而且glibc的维护者们已经花费了十五年的时间努力让fork()从POSIX异步信号安全函数列表中移除。 - Andrew Henle

1

正如用户zwol已经解释的那样,execve()不会派生一个新进程。相反,它会替换当前进程的地址空间和CPU状态, 从可执行文件名加载新的地址空间,并从main()开始执行,参数列表为argv,环境变量列表为envp。 它保留pid和打开的文件。

int execve(const char *filename,char *const argv [],char *const envp[]);

filename:可执行文件的名称
argv:命令行参数
envp:环境变量设置(例如,$PATH$HOME等)


0

posix_spawn。但它忽略了execvp()的失败——可能是因为实现这个功能被认为太复杂了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接