【问题标题】:Capturing exit status code of child process捕获子进程的退出状态码
【发布时间】:2015-02-03 02:01:44
【问题描述】:

我有一个函数,它分叉一个进程,复制输入和输出缓冲区的文件描述符,然后在通过名为 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 的退出状态,以便我可以在终止之前从父程序发出正确的错误状态代码。

这一般是怎么做的?

【问题讨论】:

    标签: c exec fork pid


    【解决方案1】:

    您可以通过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,但您必须注意何时 你这样做,因为你无法保证哪个进程将在何时运行。如果您在从c2b_popen4() 返回后立即使用WNOHANG 调用waitpid(),它可能会在您的子进程有机会执行并以错误代码终止之前返回0,并使其看起来好像执行是在即将失败时成功。

    如果进程确实立即终止,您将在读取和写入管道时遇到问题,因此一个选项是检查waitpid(),如果您在第一次尝试时遇到错误,检查是否read()write() 失败,因为您的子进程死亡。如果结果为真,您可以检索退出状态并退出整个程序。

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

    【讨论】:

    • 当我运行您的第一个代码块(“例如”示例)时,进程挂在waitpid() 调用上。
    • @AlexReynolds:默认情况下就是这样工作的,请参阅更新以回答。您需要某种策略来检测并处理子进程提前终止的可能性。
    猜你喜欢
    • 2018-06-24
    • 2020-06-20
    • 2014-11-01
    • 2020-02-27
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 2015-09-04
    • 1970-01-01
    相关资源
    最近更新 更多