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