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

Ale*_*lds 13 c fork pid exec

我有一个函数,它分叉一个进程,复制输入和输出缓冲区的文件描述符,然后运行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(),然后使用宏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);
    }
}
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()信号处理程序也是安全的)并获得其退出状态之后,可以从信号处理程序中调用.