在C中使用execve loader时子进程如何终止

时间:2018-11-15 08:21:58

标签: c linux process

我是C语言的初学者,但我在理解C语言中的execve函数以调用子进程来加载和运行可执行对象文件方面感到很苦。

我们知道execve仅在出现诸如找不到文件名之类的错误时才返回到调用程序,因此它被调用一次,永不返回。

这是我的问题,如果我们派生一个子进程来调用execve,但是由于execve永不返回,那么如果一切正常,它将始终执行某些操作,这意味着该子进程将永远不会执行被终止,那么父进程如何获得此子进程?下面是示例代码

if ((pid = Fork()) == 0) { /* Child runs user job */
   if (execve(argv[0], argv, environ) < 0) {      -------->line 2
      printf("%s: Command not found.\n", argv[0]);
      exit(0);
   }
}

/* Parent waits for foreground job to terminate */
if (waitpid(pid, &status, 0) < 0) {  ------------> but the child process never terminated
   printf("waitpid error");
}

那么在第2行中,execve(argv[0], argv, environ)永不返回,因此子进程永不终止吗?

3 个答案:

答案 0 :(得分:1)

您的程序foo将启动一些子进程来运行其他程序bar, 并且您希望它使用基本系统调用forkexecve

让我们调用您最初的foo进程 p1 。 (这代表一些pid。)

首先,您将呼叫fork。 这样会创建 p1 的子进程,该子进程正在运行foo另一个实例 将该子进程称为 p1.1

p1.1 正在运行foo。但是您想运行bar。因此,在 p1.1 中,foo立即调用execve(path/to/bar ...)。 这将用foo的实例替换 p1.1 正在运行的bar的实例。那你的 子进程 p1.1 根据需要正在运行bar

对此有所了解:-

execve(path/to/bar ...)不会在新的子过程中以 开始bar p1.1 ,并使 p1.1 仍在运行foo的后叉实例。相反,execve(path/to/bar ...) 替换 foo实例与bar实例在进程 p1.1 中。在fork之后但在execve之前, 我们有:

p1[foo] -> p1.1[foo]

execve之后,我们有:

p1[foo] -> p1.1[bar]

不是:

p1[foo] -> p1.1[foo] -> p1.1.1[bar]

然后您会看到execve 无法成功返回给其调用者 p1.1 [foo], 因为如果execve成功,则 p1.1 [foo] 不再存在。 当然,execve无法将成功返回给 p1 [foo],因为 p1 [foo] 没有调用它

  

由于execve永不返回,如果一切正常,它将始终执行某事

不。 execve p1.1 [bar]替换 p1.1 [foo],并且不返回,因为调用者不再存在。然后 p1.1 [bar]运行直到终止。

p1.1 [bar]迟早会以一种方式终止 any 程序终止:它将运行到正常的exit为止,否则它将终止 被信号杀死,或者可能调用自己的意志abort

父进程( p1 )如何获得此子进程( p1.1 )?

首先,不必。一旦 p1 [foo]启动了 p1.1 ,它可以, 如果那是您想要的,就不用管 p1.1 了,继续从事其他业务 (如果有),最后是exit。如果 p1 p1.1 之前终止,则 p1.1 成为orphan processinit process立即将孤立过程作为孩子。所以 如果在此期间没有任何终止操作,则init终止时,系统将关闭 p1.1

但是很可能您不想遗弃孤儿,而您要做希望foo知道孩子bar的退出状态。在这种情况下, p1 [foo]迟早必须致电wait/waitpid来学习 p1.1 结束,然后采取相应行动。

与此同时, p1 [foo]可能正在与 p1.1 [bar]进行一些通信 inter-process communication的形式。和/或 p1 [foo]可能 注意 p1.1 [bar]尚未结束的经过时间。通过其中一种或多种方式, p1 [foo]可能会确定 p1.1 [bar]遇到了麻烦,已经持续了太长时间,因此决定自行kill p1.1 。 当 p1.1 被杀死时-无论是谁-或以自己的意愿结束,wait/waitpid都会将该信息返回给 p1 [foo],然后 可能退出自身,或继续做其他事情。

在您要求的评论中:

  

我们是否不能像[execve那样设计:如果子进程终止,则返回1?

这样的系统调用当然可以设计并且已经存在,但是它不能 是代替呼叫过程的非阻塞系统调用,即execve 是。这将是一个阻塞系统调用,它将运行该调用的子过程 进程,并将子进程的退出状态返回给父进程。这样的人就是system

答案 1 :(得分:0)

来自execve的手册页:

  

成功时,execve()不返回,错误-1时返回,并且   errno设置正确。

要从子进程接收返回值,需要使用wait

答案 2 :(得分:0)

在简单的情况下,无论您是否成功调用exec,子进程都会最终终止。因此,这很容易……只要使用wait创建的每个孩子都有一个父亲fork

但是,有一种技术可以将故障从exec传播到父级。该技术的工作原理如下:

  1. 创建管道。
  2. 叉子。
  3. 在子进程中,关闭管道的读取侧。将写侧标记为“执行时关闭”。
  4. 给孩子打电话exec。如果失败,则将失败消息写入管道,然后退出。
  5. 在父进程中,关闭管道的写端,并从读端读取。

如果exec成功,则父进程将不读取任何数据,而仅获得EOF。如果失败,则父级将读取错误消息。