前言
如果父進程沒有結束,而子進程終止了。那么在父進程調用 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,而是因為子進程終止后,其一些信息操作系統或者用戶以后還可能會用到。