当我从 c 中的一个分叉的孩子执行()时会发生啥

Posted

技术标签:

【中文标题】当我从 c 中的一个分叉的孩子执行()时会发生啥【英文标题】:what happens when I exec() from a forked child in c当我从 c 中的一个分叉的孩子执行()时会发生什么 【发布时间】:2015-02-14 04:42:41 【问题描述】:

假设我有进程 foo 和可执行 bar。 foo 调用 fork() 现在我有两个,分别称为 foo_parent 和 foo_child。

foo_parent 调用 wait() foo_child 调用 execvp([stuff to run bar]);

我不太明白 foo_child/bar 会发生什么。 foo_child 是否用 bar 覆盖在内存中?是否启动了一个新的 bar 进程并给出了 foo_child 的 pid? bar 是 foo_child 的子节点,而 foo_child 只是将返回值传递给 foo_parent 吗?

我知道当 foo_parent 在等待后获得退出状态时,这是 bar 的 exit() 调用的结果,但我并没有很好地了解“幕后”发生的事情。

【问题讨论】:

【参考方案1】:

“foo 调用 fork(),现在我有两个,分别称为 foo_parent 和 foo_child。”

不那么令人困惑:你仍然有进程 foo(父),现在你还有一个子进程(foo_child)。

“foo 在内存中被 bar 覆盖了吗?”

当 foo_child 调用 exec() 时,它的内存空间会被 bar 的内存空间覆盖。它保留了 foo 的一些东西。特别是,在 foo 中打开的文件描述符(并且不是 close-on-exec)仍然在 bar 中打开,并且它还继承了 foo 的一些信号处理配置(有关详细信息,请参阅 exec* 的手册页)。

“是否启动了一个新的 bar 进程并给定了 foo_child 的 pid?”

有点,不完全是。新进程 foo_child 具有自己的 pid 和内存空间,在您分叉时开始。在分叉时它有一份 foo 的内存空间。当它调用 exec(bar) 时,它的内存空间被 bar 覆盖并运行 bar 的可执行代码而不是 foo。

“bar 是 foo_child 的子节点,而 foo_child 只是将返回值传递给 foo_parent 吗?”

不,bar(即 - foo_child 现在正在执行 bar)是 foo 的子进程,其退出值可以返回给 foo。

【讨论】:

Bar 获得了新的内存空间,所以它不会覆盖 foo_child 的文本部分?所以基本上创建了一个全新的进程,并从 foo_child 获得 pid(可能还有一些其他数据)? 我想起了布谷鸟,将婴儿推出巢穴并由母亲喂食:p 一个警告:在 foo 中打开的一些文件描述符在 bar 中仍然打开,并且一些信号处理处置是继承的。不维护标记为“执行时关闭”的文件,并且正在处理的任何信号都将获得默认处置重置。 @WilliamPursell 我一直和你在一起直到“一些”。 “文件描述符?” “信号处理配置?”如何在 exec 上将文件标记为关闭? (我认为这将是我与 fopen() 或类似的东西一起使用的东西,我想确保关闭,所以我不会留下打开的文件) @DanielBall 更正我之前的评论。在分叉之后,孩子在分叉时拥有父级的副本,但有自己的内存空间。如果孩子(或父母)改变了他们自己的变量,那么改变将不会反映在其他过程中。所以,是的,当 foo_child 调用 exec 时,foo_child 的内存空间会被 bar 的新二进制图像(文本和数据区域)覆盖。【参考方案2】:

foo_child 的进程被全部替换为bar。它具有相同的 PID,foo_childbar 中的许多其他属性相同,但可执行文件重新启动。进程bar仍然是foo_parent的子进程,所以foo_parent可以获取其退出状态等

请注意,exec*() 系列函数如果成功则永远不会返回。如果他们完全返回,他们就失败了。

【讨论】:

pid 是如何附加到 bar 的(就此而言,它首先是如何“附加”到 foo_parent 和 foo_child 的)? 我不知道如何解释。 PID 是进程的属性;它存储在内核的数据结构中,其中包含有关进程的所有信息。它并不像“它是进程条目数组中的索引号”那么简单,但从概念上讲,它并不太远(通常是一个稀疏数组)。 退出状态是要使用的术语。退出状态通常是 16 位值的高 8 位;低 8 位包含有关杀死进程的信号、是否被信号杀死以及是否转储内核的信息。这可以被 WIFEXITED、WIFSIGNALED、WEXISTATUS、WTERMSIG(通常是 WCOREDUMP,但 POSIX 没有标准化)拾取。 您可以将proc 结构视为进程控制块。它会在进程结构中记录新程序(或者如果您重新执行原始程序,则为原始程序),但程序的内容将保留在磁盘上。是的,exec 处理的一部分将启动进程并将程序计数器设置为bar 中代码的开头。 请注意,如果foo_parent 进程执行了另一个进程,则第二个进程将继承foo_childbar 进程,即使它可能不知道它已经这样做了.您可以在execvp() 和相关手册页中找到所有详细信息。

以上是关于当我从 c 中的一个分叉的孩子执行()时会发生啥的主要内容,如果未能解决你的问题,请参考以下文章

检查一个分叉的孩子是不是已经在 perl 中执行

C: 多个分叉

叉子后如何通过键盘在孩子中引入字符串

如何在python中杀死一个分叉的孩子及其jackd子进程

当我在 Windows 上的 HID 设备上执行 ReadFile() 时会发生啥?

为啥当我尝试将 rails 更新到 4.0.0 时会发生这种情况?