- 論壇徽章:
- 0
|
本帖最后由 duanjigang 于 2010-07-23 10:21 編輯
感謝樓上解答
其實(shí)我的本意是想知道為什么system會產(chǎn)生僵尸進(jìn)程, 比如有人碰到過、或者能從原理上講明白 ...
morris2600 發(fā)表于 2010-07-23 08:54 ![]()
首先你可以看下《linux內(nèi)核設(shè)計與實(shí)現(xiàn)》大概前幾章,有一章講這個的。另外,下面
這篇文章說的也很好。
http://hi.baidu.com/kobetec/blog ... dca883e950cdf4.html
怎樣避免產(chǎn)生僵尸進(jìn)程2008-02-20 17:09怎樣避免產(chǎn)生僵尸進(jìn)程
摘錄于: <<Advanced Programming in the UNIX® Environment: Second Edition>> By W. Richard Stevens, Stephen
1.什么是僵尸進(jìn)程?
In UNIX System terminology, a process that has terminated,but whose
parent has not yet waited for it, is called a zombie.
在UNIX 系統(tǒng)中,一個進(jìn)程結(jié)束了,但是他的父進(jìn)程沒有等待(調(diào)用wait / waitpid)他,
那么他將變成一個僵尸進(jìn)程.
但是如果該進(jìn)程的父進(jìn)程已經(jīng)先結(jié)束了,那么該進(jìn)程就不會變成僵尸進(jìn)程,
因?yàn)槊總進(jìn)程結(jié)束的時候,系統(tǒng)都會掃描當(dāng)前系統(tǒng)中所運(yùn)行的所有進(jìn)程,
看有沒有哪個進(jìn)程是剛剛結(jié)束的這個進(jìn)程的子進(jìn)程,如果是的話,就由Init
來接管他,成為他的父進(jìn)程,從而保證每個進(jìn)程都會有一個父進(jìn)程.
而Init進(jìn)程會自動wait 其子進(jìn)程,因此被Init接管的所有進(jìn)程都不會變成僵尸進(jìn)程.
2. 僵尸進(jìn)程的危害
由于子進(jìn)程的結(jié)束和父進(jìn)程的運(yùn)行是一個異步過程,即父進(jìn)程永遠(yuǎn)無法預(yù)測子進(jìn)程
到底什么時候結(jié)束. 那么不會因?yàn)楦高M(jìn)程太忙來不及waid子進(jìn)程,或者說不知道
子進(jìn)程什么時候結(jié)束,而丟失子進(jìn)程結(jié)束時的狀態(tài)信息呢?
不會.因?yàn)閁NIX提供了一種機(jī)制可以保證 只要父進(jìn)程想知道子進(jìn)程結(jié)束時的狀態(tài)信息,
就可以得到. 這種機(jī)制就是:
在每個進(jìn)程退出的時候,內(nèi)核釋放該進(jìn)程所有的資源,包括打開的文件,占用的內(nèi)存等.
但是仍然為其保留一定的信息(包括進(jìn)程號the process ID,退出狀態(tài)the termination
status of the process,運(yùn)行時間the amount of CPU time taken by the process等),
直到父進(jìn)程通過wait / waitpid來取時才釋放.
但這樣就導(dǎo)致了問題,如果你進(jìn)程不調(diào)用wait / waitpid的話, 那么保留的那段信息就不會
釋放,其進(jìn)程號就會一定被占用,但是系統(tǒng)所能使用的進(jìn)程號是有限的,如果大量的產(chǎn)生
僵死進(jìn)程,將因?yàn)闆]有可用的進(jìn)程號而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程.
此即為僵尸進(jìn)程的危害,應(yīng)當(dāng)避免.
3.僵尸進(jìn)程的避免
1、父進(jìn)程通過wait和waitpid等函數(shù)等待子進(jìn)程結(jié)束,這會導(dǎo)致父進(jìn)程掛起
2. 如果父進(jìn)程很忙,那么可以用signal函數(shù)為SIGCHLD安裝handler,因?yàn)樽舆M(jìn)程結(jié)束后,
父進(jìn)程會收到該信號,可以在handler中調(diào)用wait回收
3. 如果父進(jìn)程不關(guān)心子進(jìn)程什么時候結(jié)束,那么可以用signal(SIGCHLD, SIG_IGN)
通知內(nèi)核,自己對子進(jìn)程的結(jié)束不感興趣,那么子進(jìn)程結(jié)束后,內(nèi)核會回收,
并不再給父進(jìn)程發(fā)送信號
4. 還有一些技巧,就是fork兩次,父進(jìn)程fork一個子進(jìn)程,然后繼續(xù)工作,子進(jìn)程fork一
個孫進(jìn)程后退出,那么孫進(jìn)程被init接管,孫進(jìn)程結(jié)束后,init會回收。不過子進(jìn)程的回收
還要自己做。 下面就是Stevens給的采用兩次folk避免僵尸進(jìn)程的示例.
Example
Recall our discussion in Section 8.5 about zombie processes. If we want to write a process so that it forks a child but we don't want to wait for the child to complete and we don't want the child to become a zombie until we terminate, the trick is to call fork twice. The program in Figure 8.8 does this.
We call sleep in the second child to ensure that the first child terminates before printing the parent process ID. After a fork, either the parent or the child can continue executing; we never know which will resume execution first. If we didn't put the second child to sleep, and if it resumed execution after the fork before its parent, the parent process ID that it printed would be that of its parent, not process ID 1.
- #include "apue.h"
- #include <sys/wait.h>
- int
- main(void)
- ...{
- pid_t pid;
- if ((pid = fork()) < 0) ...{
- err_sys("fork error");
- } else if (pid == 0) ...{ /**//* first child */
- if ((pid = fork()) < 0)
- err_sys("fork error");
- else if (pid > 0)
- exit(0); /**//* parent from second fork == first child */
- /**//*
- * We're the second child; our parent becomes init as soon
- * as our real parent calls exit() in the statement above.
- * Here's where we'd continue executing, knowing that when
- * we're done, init will reap our status.
- */
- sleep(2);
- printf("second child, parent pid = %d ", getppid());
- exit(0);
- }
-
- if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */
- err_sys("waitpid error");
- /**//*
- * We're the parent (the original process); we continue executing,
- * knowing that we're not the parent of the second child.
- */
- exit(0);
- }
復(fù)制代碼 |
|