很不幸地,對於死掉的子 process 應有的行為特性並沒有辦法做一般化,因 為這些特定的機制會隨著 Unix 的種類不同而有所差異。
首先,在各種 Unix 上面您都必需使用 wait() 來處理子 process。也就是 說,我還沒看過有一種 Unix 會自動把結束的子 process 幹掉,即使您不告 訴它該怎麼做。
其次,在某些從 SysV 衍生的系統當中,如果您執行了 signal(SIGCHLD, SIG_IGN)",(嗯,事實上應該是 SIGCLD 而非 SIGCHLD,但大多數新出 爐的 SysV 系統都會在表頭檔當中加上 #define SIGCHLD SIGCLD),那 麼子 processes 都會自動被清除得乾乾淨淨,您什麼事都不用做。看看這個 方式是否可行的最佳做法就是自己在機器上試試看。如果您想試著寫出具可 攜性的程式碼,那麼依賴這種特殊處理方式可能不是好主意。不幸的是,在 POSIX 並不允許您這樣做;把 SIGCHLD 的行為特性設成 SIG_IGN 在 POSIX 當中並沒有定義,所以如果要讓您的程式合乎 POSIX 的要求時,就不可以 這樣做。
那麼怎樣才算是 POSIX 的做法呢?如同前面所述,您必需設定一個 signal 的處理函數,然後讓它去 wait。在 POSIX 當中 signal 處理函數是經由 sigaction 設定,由於您只對終止的子 process 感興趣,而不是那些 stopped 的子 process,所以可以在 sa_flags 當中加上 SA_NOCLDSTOP。如果要 wait 子 process 而本身不因此被擋 (block),可以使用 waitpid()。第一 個參數必需是 -1 (代表 wait 任何 pid),第三個參數必需是 WNOHANG,這是 最具可攜性的做法,也是可能會成為未來最具可攜性的寫法。
如果您的系統不支援 POSIX,那就有很多做法了。最簡單的方式就是先試 試 signal(SIGCHLD, SIG_IGN) 是否可行,可以的話那就好了。如果 SIG_IGN 無法用來強制自動收拾殘骸,那麼您就要自己寫一個 signal 處理 函數來收拾殘骸了。要寫出一個適用於每一種 Unix 的 singal 處理函數來 做這件事是不容易的事,因為有下列不一致的地方:
在一些 Unix 中,一個或一個以上的子 process 死時,會呼叫 SIGCHLD 的 signal 處理函數。也就是說,如果你的 signal 處理函數只有一個 wait() 時,並不會把所有的子 process 都收拾乾淨。不過還好,我相信這類的 Unix 都會有 wait3() 或 waitpid(),這兩者都有可在 option 參數中使用 WNOHNAG 可用來檢查是否有子 process 尚待收拾。所以在一個有 wait3()/waitpid() 的系統中,你可以一再重複使用 wait3()/waitpid() 以確定所有的子 process 都已收拾乾淨。最好是用 waitpid() 因為 它在 POSIX 標準中。
在一些 SysV-derived 的系統中,再 SIGCHLD 的 signal 處理函數結束後, 若還有子 process 等待清除,還是會產生 SIGCHLD signal。 因此,在大部 份的 SysV 系統中,在 signal 處理函數裡可以假設要處理的 signal 只有一 個, 反正若還有等待處理者,signal 會一再的產生,系統也會一再的呼叫 signal 處理函數。
在一些比較舊的系統中,無法防止 signal 處理函數在被呼叫後 signal 處理 函數被自動設為 SIG_DFL。在這類的系統中,要在你的signal 處理函數中 的最後加入 "signal(SIGCHLD,catcher_func)"("catcher_func" 是處理函數的 名字)。
還好新一點的系統中在 signal 處理函數被呼叫後並不會從設為 SIG_DFL。所以在沒有 wait3()/waitpid() 而有 SIGCLD 的系統中,要處理 此問題,當在處理函數呼叫了一次 wait() 以後就得用 signal() 從新設定 signal 處理函數。為了向前相容之故, System V 的 signal() 維與以前相同 的作法。所以,應該要用 sigaction() 或 sigset() 來安裝 signal 處理函數。
總結來說,在有 waitpid()(POSIX) 或wait3() 的系統了,你應該要用它們而 你的 signal 處理函數裡也要 loop,在沒有 waitpid() 與 wait3() 的系統 中,則每次呼叫 signal 處理函數都要有 wait()。
最後提供一個 portable 但是效率較差的做法。這個方法是 parent process 在 fork() 後要 wait() 它的 child process 的結束。而此 child process 馬上又 fork(),這時你就有一 child process與一 grandchild process。 將此 child process 馬上結束,則 parent process 也會跟著結束。 讓 grandchild 來做原先要 child 做的事情。此時 grandchild 的因為其 parent (child) 已死, parent 就變成了 init, init 就會幫你處理 wait() 相關事宜。這個方 法多用了一個 fork() 所以比較沒有效率,但這絕對是個 portable 的方法。