簡體   English   中英

無法獲取waitpid()以針對錯誤情況返回正確的WEXITSTATUS

[英]Not able to get waitpid() to return correct WEXITSTATUS for error condition

我有一個命令和一些輸入,在命令行上運行時將返回錯誤,並且相關錯誤代碼為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()之后的檢查”調用不會掛起,因為該進程已在此步驟中退出。

這里有幾件事。

首先,您的global_foo_status變量將在調用waitpid()或好友之后且僅在調用之后更新。 在提供的代碼中,在創建線程之前,只調用一次waitpid() 因此,您正在使用的所有WIFEXITEDWEXITSTATUS宏都在與您對waitpid()初始調用中獲得的global_foo_status值相同。 這幾乎可以肯定是為什么在調試時始終看到零值的原因,因為在進程終止后您永遠都不會獲得更新的值,而您只是反復檢查該初始值。 如果要檢查進程是否退出,則每次都必須再次調用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不必是全局變量,因為它從未在其他任何地方訪問過) )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM