无法获取waitpid()以返回错误条件的正确WEXITSTATUS

时间:2014-12-10 21:38:32

标签: c waitpid wexitstatus

我有一个命令和一些输入,当在命令行上运行时将返回错误,相关的错误代码为1:

$ foo bar
[some useful error message...]
$ echo $?
1

我正在尝试使用waitpid()

捕获此错误代码
...
char *proc_cmd = "foo bar"
pid_t proc = popen4(proc_cmd, in_fd, out_fd, err_fd, POPEN4_FLAG_NONE);
...
if (waitpid(proc, &global_foo_status, WNOHANG | WUNTRACED) == -1) {
    /* process failed */
}
...
pthread_create(&proc_thread, NULL, perform_foo_function, bar_data);
pthread_join(proc_thread, (void **) NULL);
...

我的线程将运行perform_foo_function(),直到没有更多bar_data要处理,或者直到由于数据错误导致进程失败:

static void * perform_foo_function (data *bar_data) {
    /* check before */
    if (WIFEXITED(global_foo_status)) {
        int exit_status = WEXITSTATUS(global_foo_status);
        if (exit_status != 0) 
            /* process failed */
    }

    /* do stuff with bar_data */
    while (bar_data) {
        /* causes error ... */
    }

    /* check after */
    if (WIFEXITED(global_foo_status)) {
        int exit_status = WEXITSTATUS(global_foo_status);
        if (exit_status != 0) 
            /* process failed */
    }

    pthread_exit(NULL);
}

我的问题是如何捕获此过程的错误状态?在调试过程中,WEXITSTATUS始终为零,无论我是故意创建错误情况还是提供合法输入。

我对waitpid()和相关的状态代码检查有什么误解,我应该做些什么改变才能让它发挥作用?

后续

以下代码似乎无效阻止:

...
char *proc_cmd = "foo bar"
pid_t global_foo_pid = popen4(proc_cmd, in_fd, out_fd, err_fd, POPEN4_FLAG_NONE);
...
if (waitpid(global_foo_pid, &global_foo_status, WNOHANG | WUNTRACED) == -1) {
    /* process failed */
}
...
pthread_create(&proc_thread, NULL, perform_foo_function, bar_data);
pthread_join(proc_thread, (void **) NULL);
...

static void * perform_foo_function (data *bar_data) 
{
    /* do stuff with bar_data */
    while (bar_data) {
        /* causes error ... */
    }

    /* check after */
    if (WIFEXITED(global_foo_status)) {
        waitpid(global_foo_pid, &global_foo_status, WUNTRACED);
        int exit_status = WEXITSTATUS(global_foo_status);
        if (exit_status != 0) 
            /* process failed */
    }

    pthread_exit(NULL);
}

我猜测"检查"" waitpid()来电不会挂起,因为此流程已在此步骤中退出。

1 个答案:

答案 0 :(得分:1)

这里有几件事。

首先,您的global_foo_status变量将在致电waitpid()或朋友之后,之后才会更新。在提供的代码中,您只需在创建线程之前调用waitpid()一次。因此,您使用的所有WIFEXITEDWEXITSTATUS宏都处理与global_foo_status初始调用相同的waitpid()值。这几乎可以肯定,当您进行调试时,您总是会看到零值,因为在您的流程终止后,您永远不会获得更新的值,并且您只是反复检查该初始值。如果您想检查流程是否已退出,则每次都必须再次致电waitpid()

其次,如果进程正常终止,WIFEXITED的计算结果为true,但这不是进程终止的唯一方法。还有另一个宏WIFSIGNALED,如果由于收到信号而终止进程,则会将其评估为true。如果您仅使用WIFEXITED检查终止,并且您的流程因信号异常终止,您将永远无法成功检查。更好的方法是使用waitpid()的回报来确定过程是否因任何原因而死亡。

你的功能应该看起来更像这样:

static void * perform_foo_function (data *bar_data) {

    /* check before */

    pid_t status = waitpid(global_foo_pid, &global_foo_status, WNOHANG);
    if ( status == -1 ) {
        perror("error calling waitpid()");
        exit(EXIT_FAILURE);
    }
    else if ( status == global_foo_pid ) {

        /*  Process terminated  */

        if ( WIFEXITED(global_foo_status) ) {

            /*  Process terminated normally  */

            int exit_status = WEXITSTATUS(global_foo_status);
            if ( exit_status ) {
                /*  Process failed  */

                return NULL;
            }
            else {
                /*  Process terminated normally and successfully  */

                return NULL;
            }
        }
        else {

            /*  Process terminated abnormally  */

                return NULL;
        }
    }

    /*  Process is still running if we got here  */

    /* do stuff with bar_data */

    while (bar_data) {
        /* causes error ... */
    }

    /*  Check after - if getting an error from doing stuff
        with bar_data implies the process should always
        shortly terminate, then you probably don't want
        WNOHANG in the following line.                       */

    status = waitpid(global_foo_pid, &global_foo_status, WNOHANG);
    if ( status == -1 ) {
        perror("error calling waitpid()");
        exit(EXIT_FAILURE);
    }
    else if ( status == global_foo_pid ) {

        /*  Process terminated  */

        if ( WIFEXITED(global_foo_status) ) {

            /*  Process terminated normally  */

            int exit_status = WEXITSTATUS(global_foo_status);
            if ( exit_status ) {
                /*  Process failed  */

                return NULL;
            }
            else {
                /*  Process terminated normally and successfully  */

               return NULL;
            }
        }
        else {
            /*  Process terminated abnormally  */

                return NULL;
        }
    }

    pthread_exit(NULL);
}

整个过程检查也是分解为单独函数的主要候选者。

如果你有几个线程同时运行perform_foo_function(),那么waitpid()只会在其中一个线程中正确返回。您可能需要一个单独的变量global_foo_has_finished或类似变量,线程可以在尝试调用waitpid()之前检查该变量。您还希望同步对所有这些全局变量的访问,或者重新设计以便它们不是必需的(例如,您可以将global_foo_pid传递给您的线程函数,global_foo_status不会#39; t需要是全球性的,因为它从未在其他任何地方访问过。)