0

我有一个命令和一些输入,当在命令行上运行时会返回一个错误,相关的错误代码为 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()调用不会挂起,因为该过程已经在此步骤退出。

4

1 回答 1

1

几件事,在这里。

首先,您的global_foo_status变量将在并且仅在致电waitpid()或朋友之后更新。在提供的代码中,您只需调用waitpid()一次,然后再创建线程。因此,您使用的所有这些WIFEXITED和宏都在使用与您在最初调用. 这几乎可以肯定为什么在调试时总是看到零值,因为在进程终止后您永远不会获得更新的值,而您只是一遍又一遍地检查该初始值。如果要检查进程是否退出,则每次都必须再次调用。WEXITSTATUSglobal_foo_statuswaitpid()waitpid()

其次,WIFEXITED如果进程正常终止,则评估为真,但这不是进程可以终止的唯一方式。还有另一个宏,WIFSIGNALED如果进程由于接收到信号而终止,它将评估为真。如果您仅WIFEXITED用于检查终止,并且您的进程被信号异常终止,那么您将永远无法成功地检查。更好的是使用 return fromwaitpid()来确定进程是否因任何原因而死亡。

您的函数应该看起来更像这样:

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不需要是全局的,因为它从未在其他任何地方访问过)。

于 2014-12-10T23:15:58.883 回答