簡體   English   中英

在 C 中等待子進程終止的最佳實踐

[英]Best practice for waiting for a child process termination in C

我正在編寫一個 C 庫,它在某些時候會派生另一個進程,然后等待其完成。

我想以最健壯和通用的方式編寫等待子進程完成的代碼,以處理所有可能的情況,例如調用進程產生其他子進程、接收信號等。

以下 C 代碼是否正確使用了waitpid ,即以最健壯的方式?

void waitForChildProcess(int child_pid) {
    int rc, err;
    do {
        //waiting only for my own child and only for its termination.
        //The status value is irrelevant (I think) because option '0' should mean 
        //to only wait for a child termination event 
        // and I don't care about the child's exit code:
        rc = waitpid(child_pid, NULL, 0);
        err = errno;
    } while (rc == -1 && err == EINTR); //ignoring a signal
}

是的, waitpid(child_pid, ...)是最健壯的方式。

如果子進程已經退出,它將返回 child_pid,如果發生錯誤,則返回 -1 並errnoECHILD如果子進程不存在(從未創建或已經被收割)或者不是這個進程的子進程, EINVAL如果選項(第三個參數)有一個無效值,或者EINTR如果信號被傳遞到沒有安裝 SA_RESTART 標志的信號處理程序),或者0如果指定了WNOHANG選項(第三個參數)並且子進程尚未退出.

但是,我建議稍作更改:

/* Wait for child process to exit.
 * @child_pid   Process ID of the child process
 * @status      Pointer to where the child status
 *              is stored; may be NULL
 * @return       0  if success
 *              -1  if an error occurs, see errno.
*/
int waitForChildProcess(pid_t child_pid, int *status)
{
    int rc;

    if (child_pid <= 1) {
        errno = EINVAL;
        return -1;
    }

    do {
        rc = waipid(child_pid, status, 0);
    } while (rc == -1 && errno == EINTR);
    if (rc == child_pid)
        return 0;

    /* This should not happen, but let's be careful. */
    if (rc != -1)
        errno = ECHILD;

    return -1;
}

在 Linux 和 POSIXy 系統中,進程 ID 是正整數。 正如您在man 2 waitpid手冊頁中所見,零和負 PID 表示進程組,-1 表示任何子進程。 進程 1 比較特殊, init 它永遠不會退出並設置用戶空間的其余部分。 因此,當前進程的子進程可以擁有的最小 PID 是 2。

我確實認為為這些使用正確的類型是明智的: pid_t用於進程 ID,例如size_t用於對象的內存大小(包括 say strlen()的返回值。)

提供status指針(以便調用者可以使用WIFEXITED() + WEXITSTATUS()WIFSIGNALED() + WTERMSIG() )很方便,因為任何對其不感興趣的調用者都可以提供NULL NULL明確允許用於wait()waitpid()的狀態指針。)

從技術上講,使用options==0waitpid()應該只返回子 PID 或 -1( errno )。 但是,由於檢查非常便宜,我更願意將其他所有內容都視為 ECHILD 錯誤,因為這樣可以提供最可靠的結果。

調用者可以隨意忽略返回值。 但是,如果他們想知道,如果成功,返回值為 0,否則返回 -1 並errno (並且strerror(errno)提供文本原因)。

我想以最健壯和通用的方式編寫等待子進程完成的代碼。

子進程由fork系統調用創建。 最壞的情況是SIGCHLDfork返回之前傳遞給父進程。 SIGCHLD的默認信號操作是忽略該信號,以便后續的waitpid調用無限期掛起。


在任何/多線程程序中處理子進程終止的健壯 POSIX 方法是:

  1. 在創建任何額外線程之前,主線程使用sigprocmask/pthread_sigmask阻止SIGCHLD 子線程繼承父線程的信號掩碼。 換句話說, main函數應該最早阻塞信號。 (除非您的全局 C++ 對象構造函數或平台特定的構造函數在進入main之前產生新線程,但據我所知,這超出了 C++ 標准或任何平台特定標准的范圍/要求glibc甚至可能永遠掛起,如果在進入main之前創建新線程,這glibc一個長期存在的錯誤)。
  2. 一旦創建了子進程,一個線程必須調用sigwaitsigwaitinfo來接收已掛起的SIGCHLD (如果有)或等待它。 在這種情況下,信號不會丟失。

有關此處提到的問題和解決方案的完整說明,請參閱sigwaitinfo

另請參閱名為“多線程進程中的信令”的pthread_sigmask示例。


另一個 POSIX 選項是在調用fork之前安裝SIGCHLD信號處理程序。 在信號處理程序內部,只能調用一小部分異步信號安全函數。 這通常過於嚴格,因此使用自管道技巧將信號處理委托給非信號上下文。 其他一些線程從信號處理程序read管道並在“正常”非信號上下文中處理信號


Linux 提供了signalfd系統調用,它本質上為您完成了自管道技巧,這是處理信號的最不棘手和最健壯的方法。

暫無
暫無

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

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