C - 执行 新进程映像和_exit



我有这个代码:

int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
pid_t pid = fork();
if ( !pid ) {
char *aux[] = {argv[i], NULL};
execvp(aux[0], aux);
printf("nChild %d executed %s with exit code %dn", getpid(), aux[0], i); 
_exit(i);
}
}
for (int i = 1; i < argc; i++) {
printf("Child deadn");
wait(NULL);
}
return 0;
}

运行时,我注意到子进程上的execvp之后printf(...)永远不会运行。我检查了man exec发现的地方:

"exec() 系列函数将当前进程映像替换为新的进程映像。">

这是否意味着除了printf之外,_exit(i)也没有被执行?如果是这样,我怎样才能杀死子进程?execvp 新进程映像是否解决了这个问题?

编辑:假设我想在execvp行之后做一个printf(或任何其他指令),如代码所示,有什么办法吗?

编辑[2]:为了更清楚。如果我有这个块:

int main(void) {
for (int i = 0; i < 10; i++) {
pid_t pid = fork();
if (!pid) {
printf("child %dn", getpid());
//_exit(1);
}
else {
wait(NULL);
printf("parent heren");
}
}
return 0;
}

exit(1)被评论时,事情变得疯狂。所以,我的疑问是:是否有任何情况execvp正确执行并且源子进程(调用execvp的进程)不会像_exit(n)调用那样退出,产生类似于上述块的结果。

这是否意味着除了printf之外,_exit(i)也没有被执行?

是的。除非execvp()(和其他 exec 系列函数)失败,否则它将返回并执行printf()_exit()语句。

如果是这样,我怎样才能杀死子进程?execvp 新进程映像是否解决了这个问题?

execvp()不会杀死子进程 - 它甚至不知道它要替换的进程是子进程。

在大多数实际情况下,您不想"杀死"子进程。您需要等待子进程使用wait(2)完成 - 您已经这样做了。子进程是独立的,就像您的"主"进程一样;当他们的main()返回或通过调用 exit()/_exit 等时,它们会退出,或者它们会异常终止(例如被 SIGTERM/SIGKILL 信号杀死)。

这是否意味着除了printf之外,_exit(i)也没有被执行?

正确。execvp仅在失败时返回。

如果是这样,我怎样才能杀死子进程?execvp 新进程映像是否解决了这个问题?

我认为您实际上是在问您需要做什么来确保该过程退出,因为您不能自己打电话给exit

你不需要做任何事情。现在在进程中执行的程序将在想要退出时调用exit

如果是这样,我怎样才能杀死子进程?

如果你真的在问如何终止(过早终止)进程,它是通过使用kill发送信号来完成的。发送信号的进程的 pid 由fork返回。

我想在execvp行之后做一个printf(或任何其他指令),如代码所示,有什么办法吗?

不。您的程序不再在该进程中运行。您的所有代码和变量都已替换为新程序。如果你想让这个过程输出一些东西,它必须由你执行的程序来完成。

父级可以通过创建设置了FD_CLOEXEC标志的管道(在分叉之前)来检查子项是否已成功调用exec

int pipefd[2];
pid_t pid;
int status;
if (pipe2(pipefd, O_CLOEXEC) == -1) {
perror("Can't create pipe");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("Can't create pipe");
exit(1);
}
if (!pid) {
char* child_argv[] = { argv[i], NULL };
close(pipefd[0]);
execvp(child_argv[0], child_argv);
write(pipefd[1], &errno, sizeof(errno));
_exit(1);
}
{
char buf[sizeof(errno)];
ssize_t bytes_read;
close(pipefd[1]);
bytes_read = read(pipefd[0], buf, sizeof(buf));
if (bytes_read == -1) {
/* Ignoring read errors */
}
else if (bytes_read > 0) {
/* An error occurred in the child */
if (bytes_read >= sizeof(errno)) {
memmove(&errno, buf, sizeof(errno));
perror("Can't exec");
} else {
fprintf(stderr, "Can't exec: Unknown errorn");
}
waitpid(pid, &status, 0);
exit(1);
}
}
/* The child is now running the executed program */
waitpid(pid, &status, 0);
if      ( status & 0x7F ) { fprintf(stderr, "Child killed by signal %dn", status & 0x7F); }
else if ( status >> 8   ) { fprintf(stderr, "Child exited with error %dn", status >> 8); }
else                      { fprintf(stdout, "Child completed successfullyn"); }

如果execvp调用成功,您是正确的,printf_exit调用都不会执行。但一个好的做法是检查execvp调用是否成功,如果没有,则进行故障排除。

正如评论中所说,希望该程序能够自行终止,并被您的wait调用正确收获。

仅当出于任何原因要在进程终止之前中止该进程时,才需要终止该进程。为此,您必须使用kill函数,使用fork返回的进程的 pid 和要发送的信号。

对编辑的回答

假设我想在 execvp 行之后做一个 printf(或任何其他指令),如代码所示,有什么办法吗?

否,除非execvp调用失败。在(实际上......内部)execvp之后,新程序的"文本"将被执行,它main()将运行等等......父程序的"文本"将被丢弃(手册页引用中的替换部分)。

对编辑的回答[2]

是否存在 execvp 正确执行并且源子进程(调用 execvp 的进程)不会像 _exit(n) 调用那样退出

的情况

我不确定我是否完全理解了你的新问题。
execvp执行的程序可以通过多种方式退出,exit()_exit()、从main()返回、被信号杀死......但是,例如,_exit调用将在新程序的"文本"中,而不是父程序的"文本"中。
它也可能"永远"保持活力,在这种情况下,您必须采取一些预防措施,以避免在父级的wait呼叫中被阻止。大多数时候,父母会等待SIGCHLD信号来检测孩子必须被wait,当他们希望终止时可能会kill()他们。

至于你问题的这一部分:

。产生类似于上述块的结果。

关键是您的第二个代码段不会调用execvp

在 fork() 调用之后,两个进程(父进程和子进程)继续执行具有相同状态的相同程序。所以在分叉之后,两者都会继续执行循环。因此,父进程将尝试分叉(10 - i)子进程,子进程将执行相同的操作。所以你最终会得到超过11个过程,就像我想象的那样。

注意:也许使用 gdb 来跟踪您的程序将帮助您更准确地了解事情是如何工作的。您必须在每次fork()通话之前使用set follow-fork-mode [child|parent]。这有点乏味,但可行。

最新更新