C语言 捕获子进程的退出状态码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27306764/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capturing exit status code of child process
提问by Alex Reynolds
I have a function that forks a process, duplicates file descriptors for input and output buffers, and then runs execlon a command passed in via a string called cmd:
我有一个函数,它派生一个进程,复制输入和输出缓冲区的文件描述符,然后运行execl通过名为 的字符串传入的命令cmd:
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;
}
Each of the cmdinstances process my data correctly, so long as my test inputs are correct.
cmd只要我的测试输入正确,每个实例都会正确处理我的数据。
When bad data is passed to a child process, my parent program will run to completion and exit with a non-error status code of 0.
当错误数据传递给子进程时,我的父程序将运行完成并以非错误状态代码 0 退出。
If I deliberately put in bad input — to purposefully try to get one of the cmdinstances to fail in an expected way — I'd like to know how to capture the exit status of that cmdso that I can issue the correct error status code from the parent program, before termination.
如果我故意输入错误的输入——故意尝试让其中一个cmd实例以预期的方式失败——我想知道如何捕获它的退出状态,cmd以便我可以从终止前的父程序。
How is this generally done?
这一般是怎么做的?
回答by Crowman
You can get the exit status of the child via the first argument of wait(), or the second argument of waitpid(), and then using the macros WIFEXITEDand WEXITSTATUSwith it.
您可以通过 的第一个参数wait()或 的第二个参数获取子项的退出状态waitpid(),然后使用宏WIFEXITED和WEXITSTATUS它。
For instance:
例如:
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);
}
}
A simplified working example:
一个简化的工作示例:
failprog.c:
failprog.c:
int main(void) {
return 53;
}
shellex.c:
shellex.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
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;
}
Output:
输出:
paul@thoth:~/src/sandbox$ ./shellex
exit status was 53
paul@thoth:~/src/sandbox$
waitpid()will block until the process with the supplied process ID exits. Since you're calling your function with a popen()name and passing pipes to it, presumably your child process doesn't terminate quickly, so that probably wouldn't be the right place to check it, if the call succeeded. You can pass WNOHANGas the third parameter to waitpid()to check if the process has terminated, and to return 0if the child has not yet exited, but you have to be careful about whenyou do this, since you get no guarantees about which process will run when. If you call waitpid()with WNOHANGimmediately after returning from c2b_popen4(), it may return 0before your child process has had a chance to execute and terminate with an error code, and make it look as if the execution was successful when it's just about to not be successful.
waitpid()将阻塞,直到具有提供的进程 ID 的进程退出。由于您使用popen()名称调用函数并将管道传递给它,因此您的子进程可能不会快速终止,因此如果调用成功,这可能不是检查它的正确位置。您可以将WNOHANG第三个参数传递给waitpid()以检查进程是否已终止,并0在子进程尚未退出时返回,但是您在执行此操作时必须小心,因为您无法保证哪个进程将在何时运行. 如果在 from返回后立即调用waitpid()with ,它可能会返回WNOHANGc2b_popen4()0在你的子进程有机会执行并以错误代码终止之前,让它看起来好像执行成功了,但它即将不成功。
If the process does die immediately, you'll have problems reading from and writing to your pipes, so one option would be to check waitpid()if you get an error from the first attempt to do that, to check if the read()or write()is failing because your child process died. If that turns out to be true, you can retrieve the exit status and exit your overall program then.
如果进程确实立即终止,您将在读取和写入管道时遇到问题,因此一个选择是检查waitpid()您是否在第一次尝试时遇到错误,检查read()或write()是否因为您的孩子而失败进程死了。如果事实证明这是真的,那么您可以检索退出状态并退出整个程序。
There are other possible strategies, including catching the SIGCHLD signal, since that'll be raised whenever one of your child processes dies. It would be OK, for instance, to call _exit()right from your signal handler, after waiting for the child process (calling waitpid()in a signal handler is also safe) and getting its exit status.
还有其他可能的策略,包括捕获 SIGCHLD 信号,因为只要您的子进程之一死亡,就会引发该信号。例如,_exit()在等待子进程(waitpid()在信号处理程序中调用也是安全的)并获得其退出状态后,从信号处理程序中直接调用是可以的。

