exec() 更像是替换还是子例程

  • 本文关键字:替换 子例程 exec fork exec
  • 更新时间 :
  • 英文 :


在下面的链接中,以及许多其他类似的链接中,exec通常被描述为:

分叉和可执行文件之间的区别

exec 调用是一种基本上用新程序替换整个当前进程的方法。它将程序加载到当前进程空间中,并从入口点运行它。

exec() 实际上是替换了整个程序,还是更像是执行包含所选程序的 main() 的子例程,然后返回到其原始上下文和操作?

将 exec() 描述为包含所选进程的 main() 的子例程,而不是通过将所选进程的代码合并到当前进程中来完全替换它,不是更准确吗?

# original example taken from the aforementioned link
+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
<小时 />
# wouldn't the following technically be more accurate?
# or am I misunderstanding some details with exec()?
+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | bash   |
    |                  | ls()   |
    |                  +--------+
    |                      |
    |                      | ls completes
    |                      | and the program context returns to bash's
    |                      |
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | bash   |
    |                  +--------+
    |                      |
    V                      |
+--------+                 | the child branch of bash calls exit/return
| pid=7  |                 | that follows the exec statement in the part
| ppid=4 | <---------------+ "if (fork() == 0) {...}" code branch
| bash   |
+--------+
    |
    | continues
    V

我意识到我的理解存在错误,因为如果我所说的是真的,那么在 htop 中查看时,所有生成的进程都应该是 bash 或 init 的实例,但它们不是(每个进程都有自己的名称,例如 ls 或 htop)。

那么exec()真的用一个新程序替换了调用它的整个程序吗?调用 exec() 会终止调用它的程序吗?这背后的机制是什么?

exec确实用新程序替换了当前程序。该过程被完全覆盖。没有状态可以返回。但是,说exec"隐含地杀死"调用它的过程也不准确。该过程继续执行;只是它现在正在运行一个不同的程序。它保留其旧的进程 ID。

新进程作为"子例程"执行的方法是首先fork,然后让子进程执行exec,然后让父进程wait子进程的终止。这就是在外壳中键入命令时发生的情况。这也是system库函数在后台的作用。

你的理解是不正确的。 exec系列函数完全用新进程替换了调用进程。 实际上,如果 exec 函数返回,则在启动新进程时发生错误。

新进程保留旧进程的进程 ID 以及任何打开的文件描述符。 其他一切都被替换了。

这与"调用"新程序不同。 当新程序从main返回(或调用exit)时,该过程结束。 它不会"返回"调用它的程序。

它真的取代了整个程序吗?是的,确实如此。可以用它自己替换整个程序,从而从头开始重新启动同一个程序(并部分给你你似乎有的概念)。

它不更像是执行一个子例程/函数调用,恰好体现了所选程序的 main()吗? - 在返回到自己的上下文和操作之前?不。当exec()系列函数完成其工作时,该函数永远不会返回。任何返回值都指示失败。

fork()exec家族都是独立的业务,有正当理由独立存在。也就是说,它们经常一起使用,fork()创建一个新过程,例如:

  • 现在两者都从(有效地)从 fork() 语句的返回开始执行(实际上是内核内部的某个地方,这会导致两个进程在 fork() 语句内返回)
  • 其中一个从fork()返回0,而另一个返回新进程的进程 ID
  • 其中一个,以从fork()返回为键,调用exec(),因为其目的是在不停止第一个进程/程序的情况下生成一个新的进程/程序。

它真的取代了整个程序吗?

是的。

它不更像是执行一个子例程/函数调用,恰好体现了所选程序的 main()吗? - 在返回到自己的上下文和操作之前?

不。

那么更合适的描述不是更合适的吗,只是一个体现main()的子例程?(例如,将所选的流程代码合并到当前流程中?而不是完全"替换它"?

不。

现在显然我的理解存在缺陷。因为如果我上面所说的是正确的,那么当我查看 htop 时,所有生成的过程不应该都只是 bash 或 init 的实例吗?它们不是(它们都有自己的名称,例如 ls 或 htop)

完全。

那么exec()实际上是否替换了调用它的整个程序?

是的。

所以就像调用exec()隐式杀死调用它的程序一样?

差一点。它用不同的程序替换程序,正是您读到的,"[i]t 将程序加载到当前进程空间并从入口点运行它"。

这是一个奇怪的问题。你已经准确地描述了一切,然后问它是否真的以你似乎编造的完全不同的方式工作。但是你没有解释为什么你认为它以另一种方式工作。

是的,它按照向您描述的方式工作,而不是您似乎编造的方式。如果您出于某种原因认为描述不正确,并且它实际上以其他方式工作,那么您还没有告诉我们它是什么。

最新更新