[英]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 並errno
( ECHILD
如果子進程不存在(從未創建或已經被收割)或者不是這個進程的子進程, 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==0
, waitpid()
應該只返回子 PID 或 -1( errno
)。 但是,由於檢查非常便宜,我更願意將其他所有內容都視為 ECHILD 錯誤,因為這樣可以提供最可靠的結果。
調用者可以隨意忽略返回值。 但是,如果他們想知道,如果成功,返回值為 0,否則返回 -1 並errno
(並且strerror(errno)
提供文本原因)。
我想以最健壯和通用的方式編寫等待子進程完成的代碼。
子進程由fork
系統調用創建。 最壞的情況是SIGCHLD
在fork
返回之前傳遞給父進程。 SIGCHLD
的默認信號操作是忽略該信號,以便后續的waitpid
調用無限期掛起。
在任何/多線程程序中處理子進程終止的健壯 POSIX 方法是:
sigprocmask/pthread_sigmask
阻止SIGCHLD
。 子線程繼承父線程的信號掩碼。 換句話說, main
函數應該最早阻塞信號。 (除非您的全局 C++ 對象構造函數或平台特定的構造函數在進入main
之前產生新線程,但據我所知,這超出了 C++ 標准或任何平台特定標准的范圍/要求glibc
甚至可能永遠掛起,如果在進入main
之前創建新線程,這是glibc
一個長期存在的錯誤)。sigwait
或sigwaitinfo
來接收已掛起的SIGCHLD
(如果有)或等待它。 在這種情況下,信號不會丟失。 有關此處提到的問題和解決方案的完整說明,請參閱sigwaitinfo
。
另請參閱名為“多線程進程中的信令”的pthread_sigmask
示例。
另一個 POSIX 選項是在調用fork
之前安裝SIGCHLD
信號處理程序。 在信號處理程序內部,只能調用一小部分異步信號安全函數。 這通常過於嚴格,因此使用自管道技巧將信號處理委托給非信號上下文。 其他一些線程從信號處理程序read
管道並在“正常”非信號上下文中處理信號
Linux 提供了signalfd
系統調用,它本質上為您完成了自管道技巧,這是處理信號的最不棘手和最健壯的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.