孤兒進程和僵尸進程
正常的子進程fork其父進程后,二者建立父子關系。
當子進程終結時,它會通知父進程,并清空自己所占據的內存,并在kernel里留下自己的退出信息(exit code,如果順利運行,為0;如果有錯誤或異常狀況,為>0的整數)。在這個信息里,會解釋該進程為什么退出。父進程在得知子進程終結時,有責任對該子進程使用wait系統調用。這個wait函數能從kernel中取出子進程的退出信息,并清空該信息在kernel中所占據的空間。這是正常的一般情況。
如果父進程早于子進程終結,子進程就會成為一個孤兒(orphand)進程。孤兒進程會被過繼給init進程,init進程也就成了該進程的父進程。init進程負責該子進程終結時調用wait函數。
當然,一個糟糕的程序也完全可能造成子進程的退出信息滯留在kernel中的狀況(父進程不對子進程調用wait函數),這樣的情況下,子進程成為僵尸(zombie)進程。當大量僵尸進程積累時,內存空間會被擠占。
僵尸狀態存在的意義是什么?
僵尸狀態是系統中進程的一個必然狀態,也就是自身退出后向其父進程發送消息等待回收的這一段時間,只是正常的情況下父進程能夠在很短的時間內給予處理,而僵尸進程的僵尸狀態將會持續很長的時間。
給進程設置僵尸狀態的目的是維護子進程的信息,以便父進程在以后某個時間獲取。這些信息包括子進程的進程ID、終止狀態以及資源利用信息(CPU時間,內存使用量等等)。
僵尸進程產生,留在內存中的是什么?
留在內存中的是進程的進程描述符,也就是用來描述進程的數據結構,也就是進程的task_struct實例。
為什么父進程死掉之后,父進程的子僵尸進程也沒了?
如果一個進程終止,而該進程有子進程處于僵尸狀態,那么它的所有僵尸子進程的父進程ID將被重置為1(init進程)。繼承這些子進程的init進程將清理它們(init進程將wait它們,從而去除僵尸狀態)。因為僵尸進程在父進程死掉之后成為孤兒進程,int進程負責對這些孤兒進程進行收尸的操作,應該是每隔一段時間執行一次。這也是一種出現僵尸進程的補救措施。
如何防止僵尸進程的產生?
1)想辦法wait子進程
1.在fork之后的父進程中wait子進程
2.想辦法捕獲內核在子進程結束后發送的消息,并且在處理消息的函數中來wait
void sig_chld( int signo ) {
pid_t pid;
int stat;
pid = wait(&stat);
printf("child %d exit ", pid );
return;
}
int main() {
signal(SIGCHLD, &sig_chld);
}
上述過程先在main函數中給SIGCHLD信號注冊一個信號處理函數(sig_chld),然后在子進程退出的時候,內核遞交一個SIGCHLD的時候就會被主進程捕獲而進入信號處理函數sig_chld,然后再在sig_chld中調用wait,就可以清理退出的子進程。這樣退出的子進程就不會成為僵尸進程。
下面的這個博客中提到這種wait的方法存在缺陷,正確的方法應該是調用waitpid,這兩者之間有什么區別?不知道。http://www.cnblogs.com/yuxingfirst/p/3165407.html
2)調用fork兩次。以下程序 實現了這一點。
其思想很簡單,首先fork原進程得到兒子進程,然后fork兒子進程的子進程,緊接著讓兒子進程死掉,這樣,因為兒子進程先退出,孫子進程就被init接管了,實際上與最初的父進程脫離了關系,就不會僵死了(此時孫子進程的父進程相當于是init進程)http://yejun8500.blog.163.com/blog/static/463360020104555814706/