2014-12-10 94 views
0

我有一個命令和一些輸入,當在命令行中運行會返回一個錯誤,與1相關的錯誤代碼:沒有抽到waitpid函數()返回正確WEXITSTATUS錯誤條件

$ 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

這裏有一些東西。

首先,您的global_foo_status變量會在您致電waitpid()或朋友之後(並且僅在此之後)更新。在提供的代碼中,您只能在創建線程之前調用waitpid()一次。因此,您使用的所有那些WIFEXITEDWEXITSTATUS宏正在處理的初始調用waitpid()global_foo_status的值相同。這幾乎可以肯定你爲什麼在調試時總是看到零值,因爲你的進程終止後你永遠不會獲得更新的值,而且你只是反覆檢查初始值。如果您想檢查流程是否已退出,則必須每次再次致電waitpid()

其次,WIFEXITED的計算結果爲true,如果進程正常終止,但這不是進程可以終止的唯一方式。還有另一個宏,WIFSIGNALED,如果由於接收到信號而終止進程,則該宏將被評估爲真。如果您只使用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並不需要是全球性的,因爲它從來沒有訪問其他地方)。