孤兒進程:父進程先于子進程結束(遇到return、exit、異常終止等情況時),則子進程成為孤兒進程,子進程的父進程成為init進程,稱為init進程領養孤兒進程。可以通過getppid函數來查看孤兒進程的父進程ID,即init進程的ID,init進程的ID具體是多少取決于操作系統對進程的調度,其值是不確定的。在操作系統中,init進程也不止一個,可通過ps aux詳細查看。
僵尸進程:進程終止,父進程尚未回收,子進程殘留資源(PCB)存放于內核中,變成僵尸(Zombie)進程。
特別注意,僵尸進程是不能使用kill命令清除掉的。因為kill命令只是用來終止進程的,而僵尸進程已經終止。
// orphan.c? //shell產生子進程執行該程序
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{ pid_t pid;pid = fork(); //創建子進程if (pid == 0) {while (1) {printf("I am child, my parent pid = %d\n", getppid());sleep(1); //子進程一直運行}} else if (pid > 0) {printf("I am parent, my pid is = %d\n", getpid());sleep(9);printf("------------parent going to die------------\n"); //父進程先于子進程結束,子進程變為孤兒進程} else {perror("fork");return 1; //等價于exit(1),都是結束進程,且進程結束狀態置1表示出錯}return 0;
}
[root@localhost wait]# ./orphan
I am parent, my pid is = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
I am child, my parent pid = 26900
------------parent going to die------------ //父進程正常結束
I am child, my parent pid = 1? //子進程為孤兒進程,被init進程領養,即其父進程為init進程
[root@localhost wait]# I am child, my parent pid = 1 //父進程結束,shell進程收回前臺,等待命令交互
I am child, my parent pid = 1 //子進程一直執行,為孤兒進程
I am child, my parent pid = 1
I am child, my parent pid = 1
I am child, my parent pid = 1
I am child, my parent pid = 1
//zoom.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{pid_t pid;pid = fork(); if (pid == 0) {printf("---child, my parent= %d, going to sleep 10s\n", getppid());sleep(10);printf("-------------child die--------------\n"); //子進程先正常結束} else if (pid > 0) {while (1) {printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);sleep(1);} //父進程一直運行} else {perror("fork");return 1;}return 0;
}
[root@localhost wait]# ./zoom
I am parent, pid = 27152, myson = 27153
---child, my parent= 27152, going to sleep 10s
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
-------------child die--------------? //子進程死亡
I am parent, pid = 27152, myson = 27153 //父進程一直運行,一直占據前臺,shell進程無法獲得前臺交互 ?且子進程結束后,父進程沒有對子進程殘留在內核中的PCB進行回收,從而子進程變為僵尸進程。
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
I am parent, pid = 27152, myson = 27153
[root@localhost wait] # ps aux
root???? 27152? 0.0? 0.0?? 4160?? 352 pts/1??? S+?? 03:11 ??0:00 ./zoom
root???? 27153? 0.0? 0.0????? 0???? 0 pts/1??? Z+?? 03:11?? 0:00 [zoom] <defunct>
root????? 27155? 0.0? 0.0????? 0???? 0 ???????? R??? 03:12?? 0:00 [kworker/3:0]
root????? 27163? 0.0? 0.0 107892?? 360 ???????? S??? 03:12?? 0:00 sleep 60
root ?????27164? 0.0? 0.0 123360? 1384 pts/0??? R+?? 03:12?? 0:00 ps aux
[root@localhost wait]# kill 27152?
由上可以看出,父進程27152,為S+,表示該進程在后臺運行(注意,ps aux命令是在另一個終端執行的,因此相對于另一個shell終端,父進程在后臺運行);子進程27153,為Z+,表示僵尸進程,說明該進程終止后,其殘留在內核的PCB資源沒有被父進程回收;而ps aux這個命令的進程為R+,表示在前臺運行,即就在pts/0設備終端的前臺運行。而前兩個進程屬于pts/1設備。[zoom] <defunct>? defunct表示已故的,不復存在的,但其痕跡仍然殘留在內核中,占用內存資源,因此需要做到及時對僵尸進程回收和清除。
總結:在每個進程退出的時候,?內核釋放該進程所有的資源,包括打開的文件,占用的內存等。但是仍然為其保留一定的信息(包括進程號the process ID,退出狀態the termination status of the process,運行時間the amount of CPU time taken by the process等)。直到父進程通過wait / waitpid來取時才釋放。 另外,如果父進程一直不結束(不終止),在不調用wait或waitpid的情況下,其子進程結束后會變為僵尸進程,殘留在內核中,此時若父進程結束了,那么這些僵尸進程因為沒有了父進程,就會變為孤兒進程被init進程領養,init進程就會對這些僵尸進程進行回收,然后清除。因此,父進程結束了,其子進程會被回收。孤兒進程結束后,也會被init進程回收。如果要回收一個進程,除了通過其父進程調用wait或waitpid函數外,還可以殺死其父進程,讓其變為孤兒進程,被init進程回收。
系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統不能產生新的進程. 此即為僵尸進程的危害,應當避免。可以fork兩次,?父進程fork一個子進程,然后繼續工作,子進程fork一個孫進程后退出,那么孫進程被init接管,孫進程結束后,init會回收。不過子進程的回收還要自己做。