僵屍進程:子進程終止了,但是父進程沒有回收子進程的資源PCB。使其成為僵屍進程
孤兒進程:父進程先與子進程結束了,使得子進程失去了父進程,這個時候子進程會被1號進程init進程領養,成為孤兒進程
為了防止上面兩種情況,我們應當在父進程結束之前一定要回收子進程的所有資源
所以出現了wait和waitpid
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); status是一個傳出參數。 waitpid的pid參數選擇: < -1 回收指定進程組內的任意子進程 = -1 回收任意子進程 = 0 回收和當前調用waitpid一個組的所有子進程 > 0 回收指定ID的子進程
一個進程結束的時候,會關閉所有的文件描述符,釋放所有的內存空間,但是他的PCB仍然存在,保存着子進程的一些狀態信息,如果進程正常退出,保存着正常退出的狀態,如果非正常退出,那么保存着是哪個信號導致的該進程退出,父進程可以通過wait和waitpid查看到這些信息,並且徹底清除,我們知道shell是所有進程的父進程,在shell中可以通過 $查看退出狀態編號,所以可以通過shell查看並清除,如果一個進程退出,父進程並沒有調用wait和waitpid進程清除,那么就產生了僵屍進程,正常情況下,僵屍進程都被父進程清理了
下面寫一個不正常了程序,使得父進程不結束
#include <unistd.h> #include <stdlib.h> int main(void) { pid_t pid=fork(); if(pid<0) { perror("fork"); exit(1); } if(pid>0) { /* parent */ while(1); } /* child */ return 0; }
上面中,父進程一直處於while(1)循環,使得父進程不退出,下圖查看ps aux可以發現父進程狀態為R,子進程狀態為Z(僵屍態Zombie)

對於wait或waitpid函數若調用成功則返回清理掉的子進程id,若調用出錯則返回-1。父進程調用wait或waitpid時可能會出現一下的情況:
- 阻塞(如果它的所有子進程都還在運行)。
- 帶子進程的終止信息立即返回(如果一個子進程已終止,正等待父進程讀取其終止信息)。
- 出錯立即返回(如果它沒有任何子進程)。


上圖看到了,wai成功的返回值
*******************************************************************************************
對於wait和waitpid兩個函數,有所不同的是:
- 如果父進程的所有子進程都還在運行,調用wait將使父進程阻塞,而調用waitpid時如果在options參數中指定WNOHANG可以使父進程不阻塞而立即返回0。
- wait等待第一個終止的子進程,而waitpid可以通過pid參數指定等待哪一個子進程。
所以,調用wait和waitpid不僅可以獲得子進程的終止信息,還可以使父進程阻塞等待子進程終止,起到進程間同步的作用。如果參數status不是空指針,則子進程的終止信息通過這個參數傳出,如果只是為了同步而不關心子進程的終止信息,可以將status參數指定為NULL。
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 6 int main(void) 7 { 8 pid_t pid,pid_c; 9 10 int n = 10; 11 pid = fork(); 12 13 if(pid > 0 ) 14 {/* in parent */ 15 while(1) 16 { 17 printf("I am parent %d\n",getpid()); 18 //wait是一個阻塞函數,等待回收子進程資源,如果沒有子進程,wait返回-1 19 pid_c = wait(NULL); 20 printf("wait for child %d\n",pid_c); 21 sleep(1); 22 } 23 } 24 else if(pid == 0) 25 {/* in child */ 26 printf("I am child %d\n",getpid()); 27 sleep(10); 28 } 29 30 return 0; 31 } 32 33 34 運行結果: 35 I am parent 4797 36 I am child 4798 37 wait for child 4798 38 I am parent 4797 39 wait for child -1 40 I am parent 4797 41 wait for child -1 42 I am parent 4797
孤兒進程
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(void) { pid_t pid; int n=10; pid = fork(); if(pid > 0) {//創建完之后父進程就退出了 printf("I am parent\n"); exit(0); } else if(pid == 0) {//此時父進程退出,子進程被init程序接管,該進程的父進程號變成1 while(n--) { printf("I am %d, my parent is %d\n",getpid(),getppid()); sleep(1); } } else { perror("fork"); exit(-1); } return 0; } 運行結果: I am 4813, my parent is 1 I am 4813, my parent is 1 I am 4813, my parent is 1 I am 4813, my parent is 1 I am 4813, my parent is 1 I am 4813, my parent is 1
waitpad
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 int main(void) 8 { 9 pid_t pid; 10 pid = fork(); 11 if (pid < 0) 12 { 13 perror("fork failed"); 14 exit(1); 15 } 16 17 if (pid == 0) 18 {//in child 19 int i; 20 for (i = 3; i > 0; i--) { 21 printf("This is the child %d\n",getpid()); 22 sleep(1); 23 } 24 exit(3);//返回3,運行時可以看到 25 //子進程睡眠3秒之后就退出了 26 } 27 28 else 29 {//in parent 30 int stat_val; 31 waitpid(pid, &stat_val, 0);//以阻塞方式等待回收子進程,第三個參數0,表示阻塞方式 32 if (WIFEXITED(stat_val))//正常退出 33 printf("Child exited with code %d\n", WEXITSTATUS(stat_val)); 34 else if (WIFSIGNALED(stat_val))//查看被什么信號關閉 35 printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val)); 36 } 37 return 0; 38 }
