我有这段代码:
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 %d\n", getpid(), aux[0], i);
_exit(i);
}
}
for (int i = 1; i < argc; i++) {
printf("Child dead\n");
wait(NULL);
}
return 0;
}
运行时,我发现在子进程的execvp之后永远不会运行printf(...)
。我检查了man exec
我找到的地方:
&#34; exec()系列函数用新的过程映像替换当前过程映像。&#34;
这是否意味着除了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 %d\n", getpid());
//_exit(1);
}
else {
wait(NULL);
printf("parent here\n");
}
}
return 0;
}
评论exit(1)
时,事情变得疯狂。所以,我的疑问是:是否存在execvp
正确执行的任何情况,而原始子进程(调用execvp的进程)不会像_exit(n)
调用那样退出,从而产生如上面的块中的结果。
答案 0 :(得分:2)
这是否意味着除了printf之外,_exit(i)也没有被执行?
是。除非execvp()
(以及其他exec系列函数)失败,否则它将返回,并且将执行printf()
和_exit()
语句。
如果是这样,我怎么能杀死孩子的过程? execvp是否会处理新的过程映像?
execvp()
不会杀死子进程 - 它甚至不知道它替换的进程是子进程。
在大多数实际情况中,您不希望“杀死”子进程。您希望等待以使用wait(2)
来完成子进程 - 您已经这样做了。子进程是独立的,就像你的“主要”进程一样;它们会在main()
返回时退出,或者通过调用exit()/ _ exit等退出,或者它们异常终止(例如被SIGTERM / SIGKILL信号杀死)。
答案 1 :(得分:2)
这是否意味着除了printf之外,_exit(i)也没有被执行?
正确。 execvp
仅在失败时返回。
如果是这样,我该如何杀死子进程? execvp是否会处理新的过程映像?
我认为您实际上已经在询问您需要做些什么来确保流程退出,因为您无法自己致电exit
。
你不需要做任何事情。现在正在执行的程序将在它想要退出时调用exit
。
如果是这样,我该如何杀死子进程?
如果你真的在问如何杀死(过早终止)这个过程,可以通过kill
发送信号来完成。发送信号的进程的pid由fork
返回。
我想在execvp之后执行printf(或任何其他指令),如代码所示,有什么办法吗?
没有。您的程序不再在该过程中运行。您的所有代码和变量都已替换为新程序。如果您希望该过程输出某些内容,则必须由您执行的程序完成。
父级可以通过创建一个设置了exec
标志的管道(在fork之前)来检查子级是否已成功调用FD_CLOEXEC
。
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 error\n");
}
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 %d\n", status & 0x7F); }
else if ( status >> 8 ) { fprintf(stderr, "Child exited with error %d\n", status >> 8); }
else { fprintf(stdout, "Child completed successfully\n"); }
答案 2 :(得分:0)
如果execvp
调用成功,则表示您是正确的,printf
和_exit
调用都不会执行。但一个好的做法是检查execvp
调用是否成功,如果不成功则进行故障排除。
正如评论中所述,该计划有望终止,并通过wait
来电正确收到。
只有在出于任何原因想要在终止之前中止它之前,才有必要杀死该过程。为此,您必须使用kill
函数,使用fork
返回的进程的pid以及您要发送的信号。
回答编辑
假设我想在execvp行之后执行printf(或任何其他指令),如代码所示,有什么办法吗?
不,除非execvp
调用失败。在(内部,实际上......)之后execvp
,新程序的“文本”将被执行,它将main()
运行等...父程序的“文本”将被丢弃(手册报价中的替换部分)。
回答编辑[2]
是否存在execvp正确执行的任何情况,而原始子进程(调用execvp的进程)不会像_exit(n)调用中那样退出
我不确定我完全理解你的新问题
execvp
执行的程序可以通过exit()
,_exit()
以多种方式退出,从main()
返回,被信号杀死......但是{{1}例如,调用将出现在新程序的“文本”中,而不是父程序的“文本”中
它也可以“永远”活着,在这种情况下,你必须采取一些预防措施,以避免在父母的_exit
电话中被阻止。大多数情况下,父母会等待wait
信号检测到孩子必须SIGCHLD
- 并且当他们希望终止时可能wait
。
至于你问题的这一部分:
...产生的结果就像上面的块一样。
关键是您的第二个代码段不会调用kill()
。
fork()调用后,两个进程(父进程和子进程)继续执行相同的程序,具有相同的状态。所以在分叉后,两者都将继续执行循环。因此父进程将尝试fork execvp
子进程,子进程将执行相同的操作。所以你最终会有超过11个进程,就像我想象的那样。
注意:也许使用gdb跟踪您的程序将有助于您更准确地了解工作原理。您必须在每次(10 - i)
来电之前使用set follow-fork-mode [child|parent]
。这有点乏味但可行。