fork兩次如何避免僵尸進(jìn)程收藏
曾經(jīng)覺得費解,,溫故知新一下: /*g++ -o New0001 New0001.cpp */
#include <stdio.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> int main(void)
{ pid_t pid; if ((pid = fork()) < 0)
{ fprintf(stderr,"Fork error!\n"); exit(-1); } else if (pid == 0) /* first child */ { if ((pid = fork()) < 0) { fprintf(stderr,"Fork error!\n"); exit(-1); } 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\n", getppid()); exit(0); } if (waitpid(pid, NULL, 0) != pid) /* wait for first child */ { fprintf(stderr,"Waitpid error!\n"); exit(-1); } /*
* We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); } fork兩次在防止僵死方面來說,,就是因為兒子進(jìn)程先退出,孫子進(jìn)程就被init接管了,,實際上與最初的父進(jìn)程脫離了關(guān)系,,就不會僵死了。見APUE 的P151以下描述:
回憶一下8 . 5節(jié)中有關(guān)僵死進(jìn)程的討論,。如果一個進(jìn)程要f o r k一個子進(jìn)程,,但不要求它等待
子進(jìn)程終止,也不希望子進(jìn)程處于僵死狀態(tài)直到父進(jìn)程終止,,實現(xiàn)這一要求的訣竅是調(diào)用f o r k 兩次,。程序8 - 5實現(xiàn)了這一點,。 在第二個子進(jìn)程中調(diào)用s l e e p以保證在打印父進(jìn)程I D時第一個子進(jìn)程已終止。在f o r k之后,, 父,、子進(jìn)程都可繼續(xù)執(zhí)行——我們無法預(yù)知哪一個會先執(zhí)行。如果不使第二個子進(jìn)程睡眠,,則 在f o r k之后,,它可能比其父進(jìn)程先執(zhí)行,于是它打印的父進(jìn)程I D將是創(chuàng)建它的父進(jìn)程,,而不是 i n i t進(jìn)程(進(jìn)程ID 1),。 兩次fork的其它作用就是做daemon:
void InitAsDaemon()
{ if (Fork() > 0) exit(0); setsid();
Signal(SIGINT, SIG_IGN);
Signal(SIGHUP, SIG_IGN); Signal(SIGQUIT, SIG_IGN); Signal(SIGPIPE, SIG_IGN); Signal(SIGTTOU, SIG_IGN); Signal(SIGTTIN, SIG_IGN); Signal(SIGCHLD, SIG_IGN); if (Fork() > 0)
exit(0); chdir("/");
umask(0); } 關(guān)于僵尸進(jìn)程:
在fork()/execve()過程中,假設(shè)子進(jìn)程結(jié)束時父進(jìn)程仍存在,,而父進(jìn)程fork()之前既沒安裝SIGCHLD信號處理函數(shù)調(diào)用waitpid()等待子進(jìn)程結(jié)束,,又沒有顯式忽略該信號,則子進(jìn)程成為僵尸進(jìn)程,,無法正常結(jié)束,,此時即使是root身份kill -9也不能殺死僵尸進(jìn)程。補救辦法是殺死僵尸進(jìn)程的父進(jìn)程(僵尸進(jìn)程的父進(jìn)程必然存在),,僵尸進(jìn)程成為"孤兒進(jìn)程",,過繼給1號進(jìn)程init,init始終會負(fù)責(zé)清理僵尸進(jìn)程,。
僵尸進(jìn)程是指的父進(jìn)程已經(jīng)退出,而該進(jìn)程dead之后沒有進(jìn)程接受,就成為僵尸進(jìn)程.(zombie)進(jìn)程 怎樣產(chǎn)生僵尸進(jìn)程的:
一個進(jìn)程在調(diào)用exit命令結(jié)束自己的生命的時候,,其實它并沒有真正的被銷毀,而是留下一個稱為僵尸進(jìn)程(Zombie)的數(shù)據(jù)結(jié)構(gòu)(系統(tǒng)調(diào)用exit,,它的作用是使進(jìn)程退出,,但也僅僅限于將一個正常的進(jìn)程變成一個僵尸進(jìn)程,并不能將其完全銷毀),。在Linux進(jìn)程的狀態(tài)中,,僵尸進(jìn)程
是非常特殊的一種,它已經(jīng)放棄了幾乎所有內(nèi)存空間,,沒有任何可執(zhí)行代碼,,也不能被調(diào)度,僅僅在進(jìn)程列表中保留一個位置,,記載該進(jìn)程的退
出狀態(tài)等信息供其他進(jìn)程收集,除此之外,,僵尸進(jìn)程不再占有任何內(nèi)存空間,。它需要它的父進(jìn)程來為它收尸,如果他的父進(jìn)程沒安裝SIGCHLD信
號處理函數(shù)調(diào)用wait或waitpid()等待子進(jìn)程結(jié)束,,又沒有顯式忽略該信號,,那么它就一直保持僵尸狀態(tài),,如果這時父進(jìn)程結(jié)束了,那么init進(jìn)程自動
會接手這個子進(jìn)程,,為它收尸,,它還是能被清除的。但是如果如果父進(jìn)程是一個循環(huán),,不會結(jié)束,,那么子進(jìn)程就會一直保持僵尸狀態(tài),這就是為什么系統(tǒng)中有時會有很多的僵尸進(jìn)程,。
怎么查看僵尸進(jìn)程:
利用命令ps,,可以看到有標(biāo)記為Z的進(jìn)程就是僵尸進(jìn)程。
怎樣來清除僵尸進(jìn)程:
1.改寫父進(jìn)程,,在子進(jìn)程死后要為它收尸,。具體做法是接管SIGCHLD信號。子進(jìn)程死后,,會發(fā)送SIGCHLD信號給父進(jìn)程,,父進(jìn)程收到此信號后,執(zhí)行waitpid()函數(shù)為子進(jìn)程收尸,。這是基于這樣的原理:就算父進(jìn)程沒有調(diào)用wait,,內(nèi)核也會向它發(fā)送SIGCHLD消息,盡管對的默認(rèn)處理是忽略,,如果想響應(yīng)這個消息,,可以設(shè)置一個處理函數(shù)。
2.把父進(jìn)程殺掉,。父進(jìn)程死后,,僵尸進(jìn)程成為"孤兒進(jìn)程",過繼給1號進(jìn)程init,,init始終會負(fù)責(zé)清理僵尸進(jìn)程.它產(chǎn)生的所有僵尸進(jìn)程也跟著消失,。
===========================================
在Linux中可以用
ps auwx
發(fā)現(xiàn)僵尸進(jìn)程
a all w/ tty, including other users 所有窗口和終端,包括其他用戶的進(jìn)程
u user-oriented 面向用戶(用戶友好)
-w,w wide output 寬格式輸出
x processes w/o controlling ttys
在僵尸進(jìn)程后面 會標(biāo)注
ps axf
看進(jìn)程樹,,以樹形方式現(xiàn)實進(jìn)程列表
ps axm
會把線程列出來,在linux下進(jìn)程和線程是統(tǒng)一的,,是輕量級進(jìn)程的兩種方式。
ps axu
顯示進(jìn)程的詳細(xì)狀態(tài)
===========================================
killall
kill -15
kill -9
一般都不能殺掉 defunct進(jìn)程
用了kill -15,kill -9以后 之后反而會多出更多的僵尸進(jìn)程
kill -kill pid
fuser -k pid
可以考慮殺死他的parent process,,
kill -9 他的parent process
===========================================
一個已經(jīng)終止,但是其父進(jìn)程尚未對其進(jìn)行善后處理(獲取終止子進(jìn)程的有關(guān)信息,、釋放它仍占用的資源)的進(jìn)程被稱為僵死進(jìn)程(Zombie Process)。
避免zombie的方法:
1)在SVR4中,,如果調(diào)用signal或sigset將SIGCHLD的配置設(shè)置為忽略,則不會產(chǎn)生僵死子進(jìn)程,。另外,使用SVR4版的sigaction,則可設(shè)置SA_NOCLDWAIT標(biāo)志以避免子進(jìn)程僵死。
Linux中也可使用這個,,在一個程序的開始調(diào)用這個函數(shù)
signal(SIGCHLD,SIG_IGN);
2)調(diào)用fork兩次,。程序8 - 5 實現(xiàn)了這一點,。
3)用waitpid等待子進(jìn)程返回.
===========================================
zombie進(jìn)程是僵死進(jìn)程。防止它的辦法,,一是用wait,waitpid之類的函數(shù)獲得
進(jìn)程的終止?fàn)顟B(tài),,以釋放資源。另一個是fork兩次
===========================================
defunct進(jìn)程只是在process table里還有一個記錄,,其他的資源沒有占用,,除非你的系統(tǒng)的process個數(shù)的限制已經(jīng)快超過了,zombie進(jìn)程不會有更多的壞處,。
可能唯一的方法就是reboot系統(tǒng)可以消除zombie進(jìn)程,。
===========================================
任何程序都有僵尸狀態(tài),它占用一點內(nèi)存資源(也就是進(jìn)程表里還有一個記錄),,僅僅是表象而已不必害怕,。如果程序有問題有機會遇見,解決大批量僵尸簡單有效的辦法是重起,。kill是無任何效果的
fork與zombie/defunct"
在Unix下的一些進(jìn)程的運作方式,。當(dāng)一個進(jìn)程死亡時,它并不是完全的消失了。進(jìn)程終止,它不再運行,但是還有一些殘留的小東西等待父進(jìn)程收回,。這些殘留的東西包括子進(jìn)程的返回值和其他的一些東西,。當(dāng)父進(jìn)程 fork() 一個子進(jìn)程后,它必須用 wait() 或者 waitpid() 等待子進(jìn)程退出。正是這個 wait() 動作來讓子進(jìn)程的殘留物消失,。
自然的,,在上述規(guī)則之外有個例外:父進(jìn)程可以忽略 SIGCLD 軟中斷而不必要 wait()??梢赃@樣做到(在支持它的系統(tǒng)上,比如Linux):
main()
{
signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
.
.
fork();
fork();
fork(); /* Rabbits, rabbits, rabbits! */
?。?
現(xiàn)在,子進(jìn)程死亡時父進(jìn)程沒有 wait(),,通常用 ps 可以看到它被顯示為“”,。它將永遠(yuǎn)保持這樣 直到 父進(jìn)程 wait(),或者按以下方法處理,。
這里是你必須知道的另一個規(guī)則:當(dāng)父進(jìn)程在它wait()子進(jìn)程之前死亡了(假定它沒有忽略 SIGCLD),子進(jìn)程將把 init(pid 1)進(jìn)程作為它的父進(jìn)程,。如果子進(jìn)程工作得很好并能夠控制,這并不是問題,。但如果子進(jìn)程已經(jīng)是 defunct,,我們就有了一點小麻煩???,原先的父進(jìn)程不可能再 wait(),因為它已經(jīng)消亡了,。這樣,,init 怎么知道 wait() 這些 zombie 進(jìn)程。
答案:不可預(yù)料的,。在一些系統(tǒng)上,,init周期性的破壞掉它所有的defunct進(jìn)程。在另外一些系統(tǒng)中,它干脆拒絕成為任何defunct進(jìn)程的父進(jìn)程,,而是馬上毀滅它們,。如果你使用上述系統(tǒng)的一種,可以寫一個簡單的循環(huán),用屬于init的defunct進(jìn)程填滿進(jìn)程表,。這大概不會令你的系統(tǒng)管理員很高興吧?
你的任務(wù):確定你的父進(jìn)程不要忽略 SIGCLD,,也不要 wait() 它 fork() 的所有進(jìn)程。不過,,你也未必 要 總是這樣做(比如,,你要起一個 daemon 或是別的什么東西),但是你必須小心編程,如果你是一個 fork() 的新手,。另外,,也不要在心理上有任何束縛。
總結(jié):
子進(jìn)程成為 defunct 直到父進(jìn)程 wait(),,除非父進(jìn)程忽略了 SIGCLD ,。
更進(jìn)一步,父進(jìn)程沒有 wait() 就消亡(仍假設(shè)父進(jìn)程沒有忽略 SIGCLD )的子進(jìn)程(活動的或者 defunct)成為 init 的子進(jìn)程,,init 用重手法處理它們,。
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/eroswang/archive/2008/11/19/3333617.aspx
|
|