前言
如果父程式沒有結束,而子程式終止了。那麼在父程式呼叫 wait 函式回收這個子程式或者父程式終止以前,這個子程式將一直是殭屍程式。
本文將提供兩種方法處理這個問題。
方法一:父程式回收法
wait函式將使其呼叫者阻塞,直到其某個子程式終止。故父程式可呼叫wait函式回收其殭屍子程式。除此之外,waitpid函式提供更為詳盡的功能( 增加了非阻塞功能以及指定等待功能 ),請讀者自行查閱相關資料。
程式碼實現
1 #include <unistd.h> 2 #include <sys/wait.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 int main() 7 { 8 int pid; 9 int *status; 10 11 printf("%s\n", "啟動父程式"); 12 13 if ((pid = fork()) < 0) { 14 printf("%s\n", "建立子程式失敗"); 15 exit(1); 16 } 17 else 18 if (pid ==0) { 19 printf("%s\n", "進入子程式"); 20 sleep(4); 21 // 終止子程式 22 exit(0); 23 } 24 else { 25 // 進入父程式 26 // 回收殭屍子子程式 27 wait(status); 28 printf("%s\n", "回收完畢"); 29 } 30 31 exit(0); 32 }
執行測試
結果分析
第三行的“回收完畢”是在程式執行四秒後才顯示的。這說明儘管我將子程式阻塞了4秒,父程式並不會先於子程式終止。因為它呼叫了wait函式,故需要等待一個子程式結束並將其回收,否則就一直阻塞在那裡。
方法二:init程式回收法
上面的這種解決方案需要父程式去等待子程式,但在很多情況下,這並不合適,因為父程式也許還有其他任務要做,不能阻塞在這裡。在講述下面這種不用父程式等待就能完成回收子程式的方法之前,先請明白以下兩個概念:
1. 如果父程式先於子程式結束,那麼子程式的父程式自動改為 init 程式。
2. 如果 init 的子程式結束,則 init 程式會自動回收其子程式的資源而不是讓它變成殭屍程式。
程式碼實現
1 #include "apue.h" 2 #include <sys/wait.h> 3 4 int 5 main(void) 6 { 7 pid_t pid; 8 9 if ((pid = fork()) < 0) { // 建立第一個子程式 10 err_sys("fork error"); 11 } else if (pid == 0) { // 進入第一個子程式 12 if ((pid = fork()) < 0) // 建立第二個子程式 13 err_sys("fork error"); 14 else if (pid > 0) // 進入第一個子程式 15 exit(0); // 終止第一個子程式 16 // 第二個子程式在睡眠2S後才執行,這樣一般情況下第一個子程式會先終止。 17 sleep(2); 18 // 這時,第一個子程式肯定終止了,那麼它的父程式就自動變成了init。 19 printf("second child, parent pid = %d\n", getppid()); 20 exit(0); 21 } 22 23 // 父程式等待並回收第一個子程式 24 if (waitpid(pid, NULL, 0) != pid) 25 err_sys("waitpid error"); 26 27 // 父程式執行到這裡以後,可以退出,也可以執行其他的任務。 28 // 對於剛才那第二個子程式,它繼承了父程式的資源,同時它終止後也會被init程式回收, 29 // 不會成為殭屍程式。 30 exit(0); 31 }
說明
1. fork建立子程式以後,子程式擁有的是父程式的一個資源副本,而不是和它共享資源。
2. 子程式終止後變成殭屍程式並不是系統BUG,而是因為子程式終止後,其一些資訊作業系統或者使用者以後還可能會用到。