捕获子进程的退出状态代码 [英] Capturing exit status code of child process

查看:191
本文介绍了捕获子进程的退出状态代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数可以分叉一个进程,复制输入和输出缓冲区的文件描述符,然后在通过名为cmd的字符串传入的命令上运行execl:

I have a function that forks a process, duplicates file descriptors for input and output buffers, and then runs execl on a command passed in via a string called 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实例都可以正确处理我的数据,只要我的测试输入正确即可.

Each of the cmd instances process my data correctly, so long as my test inputs are correct.

当错误数据传递给子进程时,我的父程序将运行完成并以0的无错误状态代码退出.

When bad data is passed to a child process, my parent program will run to completion and exit with a non-error status code of 0.

如果我故意输入错误的信息(有意尝试使cmd实例之一以预期的方式失败),我想知道如何捕获该cmd的退出状态,以便可以在终止之前从父程序发出正确的错误状态代码.

If I deliberately put in bad input — to purposefully try to get one of the cmd instances to fail in an expected way — I'd like to know how to capture the exit status of that cmd so that I can issue the correct error status code from the parent program, before termination.

通常如何进行?

推荐答案

您可以通过wait()的第一个参数或waitpid()的第二个参数,然后使用宏<,来获得孩子的退出状态. c7>和WEXITSTATUS.

You can get the exit status of the child via the first argument of wait(), or the second argument of waitpid(), and then using the macros WIFEXITED and WEXITSTATUS with it.

例如:

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() will block until the process with the supplied process ID exits. Since you're calling your function with a popen() name and passing pipes to it, presumably your child process doesn't terminate quickly, so that probably wouldn't be the right place to check it, if the call succeeded. You can pass WNOHANG as the third parameter to waitpid() to check if the process has terminated, and to return 0 if the child has not yet exited, but you have to be careful about when you do this, since you get no guarantees about which process will run when. If you call waitpid() with WNOHANG immediately after returning from c2b_popen4(), it may return 0 before your child process has had a chance to execute and terminate with an error code, and make it look as if the execution was successful when it's just about to not be successful.

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

If the process does die immediately, you'll have problems reading from and writing to your pipes, so one option would be to check waitpid() if you get an error from the first attempt to do that, to check if the read() or write() is failing because your child process died. If that turns out to be true, you can retrieve the exit status and exit your overall program then.

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

There are other possible strategies, including catching the SIGCHLD signal, since that'll be raised whenever one of your child processes dies. It would be OK, for instance, to call _exit() right from your signal handler, after waiting for the child process (calling waitpid() in a signal handler is also safe) and getting its exit status.

这篇关于捕获子进程的退出状态代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆