2014-12-05 87 views
7

我有一个派生一个处理的功能,重复文件描述符的输入和输出缓冲器,然后运行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; 
} 

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

当坏数据传递给一个子进程,我的父母程序将运行到完成并退出与0

非错误状态代码如果我故意把在恶劣的输入 - 有目的地试图让在cmd实例中的一个以预期的方式失败 - 我想知道如何捕捉cmd的退出状态,这样我可以终止前发出从父程序正确的错误状态代码。

这是怎么一般都做?

回答

11

您可以通过的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 <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; 
} 

输出:

[email protected]:~/src/sandbox$ ./shellex 
exit status was 53 
[email protected]:~/src/sandbox$ 

waitpid()将阻塞,直到具有提供的进程ID的进程退出。既然你有popen()对骂你的函数并通过管道将它,想必你的孩子的过程不会很快结束,可能是这不会是检查它在正确的地方,如果调用成功。您可以通过WNOHANG作为第三个参数waitpid()来检查过程是否已终止,并且如果孩子还没有退出,则返回0但您必须小心当您这样做时,因为您没有得到任何保证哪个进程在什么时候运行。如果从c2b_popen4()返回后立即调用waitpid()WNOHANG,它可能会返回0你的子进程有机会与一个错误代码执行和终止之前,并使它看起来好像成功执行时,它只是不被成功的。

如果该过程立即死亡,您将在读取和写入管道时遇到问题,因此一种方法是检查waitpid(),如果在第一次尝试时出现错误,请检查read()write()由于您的子进程死亡而失败。如果结果如此,您可以检索退出状态并退出整个程序。

还有其他可能的策略,包括捕获SIGCHLD信号,因为无论何时你的子进程死亡都会引发这种策略。例如,在等待子进程(在信号处理程序中调用waitpid()也是安全的)并获取其退出状态之后,从信号处理程序中直接调用_exit()即可。

+1

当我运行你的第一个代码块(例如“例子”)时,进程挂在'waitpid()'调用上。 – 2014-12-05 07:01:50

+1

@AlexReynolds:这是默认情况下的工作方式,请参阅更新以回答。您需要某种策略来检测并处理子进程提前终止的可能性。 – 2014-12-05 07:35:26