我有一个函数,它分叉一个进程,复制输入和输出缓冲区的文件描述符,然后运行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;
}
Run Code Online (Sandbox Code Playgroud)
cmd
只要我的测试输入正确,每个实例都会正确处理我的数据.
当坏数据传递给子进程时,我的父程序将运行完成并以非错误状态代码0退出.
如果我故意输入错误的输入 - 故意尝试让其中一个cmd
实例以预期的方式失败 - 我想知道如何捕获它的退出状态,cmd
以便我可以从中发出正确的错误状态代码父程序,在终止之前.
这通常怎么做?
Cro*_*man 15
您可以通过第一个参数wait()
或第二个参数获取子项的退出状态waitpid()
,然后使用宏WIFEXITED
并WEXITSTATUS
使用它.
例如:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
一个简化的工作示例:
failprog.c
:
int main(void) {
return 53;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
paul@thoth:~/src/sandbox$ ./shellex
exit status was 53
paul@thoth:~/src/sandbox$
Run Code Online (Sandbox Code Playgroud)
waitpid()
将阻止,直到具有提供的进程ID的进程退出.由于您使用popen()
名称调用函数并将管道传递给它,因此可能是您的子进程没有快速终止,因此如果调用成功,可能不是检查它的正确位置.您可以WNOHANG
作为第三个参数传递waitpid()
以检查进程是否已终止,并0
在子进程尚未退出时返回,但是在执行此操作时必须要小心,因为您无法保证在执行此操作时将运行哪个进程.如果调用waitpid()
与WNOHANG
从回国后立即c2b_popen4()
的,可以返还0
你的子进程有机会执行和错误代码终止之前,并使它看起来好像执行成功,当它正要不会成功.
如果过程不立即死了,你将有读取和写入到您的管道问题,所以其中一个方案是要检查waitpid()
,如果你从第一次尝试这样做,得到一个错误,以检查read()
或write()
失败,因为你的孩子过程死了.如果结果是真的,那么您可以检索退出状态并退出整个程序.
还有其他可能的策略,包括捕获SIGCHLD信号,因为只要您的一个子进程死亡,就会引发该信号.例如,_exit()
在等待子进程(调用waitpid()
信号处理程序也是安全的)并获得其退出状态之后,可以从信号处理程序中调用.