捕获子进程的退出状态代码

我有一个函数,它分叉一个进程,复制输入和输出缓冲区的文件描述符,然后通过一个名为cmd的字符串传入的命令运行execl

 static pid_t c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags) { pid_t ret = fork(); if (ret < 0) { fprintf(stderr, "fork() failed!\n"); return ret; } else if (ret == 0) { /* Assign file descriptors to child pipes (not shown)... */ execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); fprintf(stderr, "execl() failed!\n"); exit(EXIT_FAILURE); } else { /* Close parent read and write pipes (not shown)... */ return ret; } return ret; } 

只要我的测试输入正确,每个cmd实例都会正确处理我的数据。

当坏数据传递给子进程时,我的父程序将运行完成并以非错误状态代码0退出。

如果我故意输入错误的输入 – 故意尝试让其中一个cmd实例以预期的方式失败 – 我想知道如何捕获该cmd的退出状态,以便我可以发出正确的错误状态代码来自父程序,在终止之前。

这通常怎么做?

您可以通过wait()的第一个参数或waitpid()的第二个参数获取子项的退出状态,然后使用宏WIFEXITEDWEXITSTATUS

例如:

 pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0); if ( ret > 0 ) { int status; if ( waitpid(ret, &status, 0) == -1 ) { perror("waitpid() failed"); exit(EXIT_FAILURE); } if ( WIFEXITED(status) ) { int es = WEXITSTATUS(status); printf("Exit status was %d\n", es); } } 

一个简化的工作示例:

failprog.c

 int main(void) { return 53; } 

shellex.c

 #include  #include  #include  #include  #include  int main(void) { pid_t p = fork(); if ( p == -1 ) { perror("fork failed"); return EXIT_FAILURE; } else if ( p == 0 ) { execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL"); return EXIT_FAILURE; } int status; if ( waitpid(p, &status, 0) == -1 ) { perror("waitpid failed"); return EXIT_FAILURE; } if ( WIFEXITED(status) ) { const int es = WEXITSTATUS(status); printf("exit status was %d\n", es); } return EXIT_SUCCESS; } 

输出:

 paul@thoth:~/src/sandbox$ ./shellex exit status was 53 paul@thoth:~/src/sandbox$ 

waitpid()将阻塞,直到具有提供的进程ID的进程退出。 由于您使用popen()名称调用函数并向其传递管道,因此可能您的子进程不会快速终止,因此如果调用成功,可能不是检查它的正确位置。 您可以将WNOHANG作为第三个参数传递给waitpid()来检查进程是否已经终止,如果孩子还没有退出则返回0 ,但是你这样做你必须要小心,因为你不能保证哪个进程将在何时运行。 如果从c2b_popen4()返回后立即使用WNOHANG调用waitpid() ,它可能会在您的子进程有机会执行并以错误代码终止之前返回0 ,并使其看起来好像执行成功时只是即将不成功。

如果进程确实立即死亡,那么读取和写入管道时会遇到问题,因此如果第一次尝试时出现错误,则检查waitpid() ,检查read()或者write()失败,因为您的子进程已经死亡。 如果结果是真的,那么您可以检索退出状态并退出整个程序。

还有其他可能的策略,包括捕获SIGCHLD信号,因为只要您的一个子进程死亡,就会引发该信号。 例如,在等待子进程(在信号处理程序中调用waitpid()也是安全的)并获得其退出状态之后,可以直接从信号处理程序调用_exit()