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

时间:2014-12-05 00:25:29

标签: c exec fork pid

我有一个函数,它分叉一个进程,复制输入和输出缓冲区的文件描述符,然后对通过名为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实例以预期的方式失败 - 我想知道如何捕获{{1}的退出状态所以我可以在终止之前从父程序发出正确的错误状态代码。

这是如何做到的?

1 个答案:

答案 0 :(得分:13)

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

输出:

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

waitpid()将一直阻止,直到具有提供的进程ID的进程退出。由于您正在使用popen()名称调用您的函数并将管道传递给它,因此可能您的子进程无法快速终止,因此可能不是检查它的正确位置,如果通话成功。您可以将WNOHANG作为第三个参数传递给waitpid()以检查进程是否已终止,如果孩子尚未退出则返回0,但您必须小心< em>当执行此操作时,因为您无法保证何时会运行哪个进程。如果您在从waitpid()返回后立即使用WNOHANG致电c2b_popen4(),则可能会在您的子进程有机会执行并以错误代码终止之前返回0,并且当它即将不成功时,让它看起来好像执行成功了。

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

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