僵尸進(jìn)程以及孤兒進(jìn)程

我們知道在unix/linux中,子進(jìn)程的結(jié)束和父進(jìn)程的運(yùn)行是一個(gè)異步過程, 當(dāng)一個(gè)進(jìn)程完成它的工作終止之后籽暇,它的父進(jìn)程需要調(diào)用wait()或者waitpid()系統(tǒng)調(diào)用取得子進(jìn)程的終止?fàn)顟B(tài)侍郭。

孤兒進(jìn)程:一個(gè)父進(jìn)程退出鸠珠,而它的一個(gè)或多個(gè)子進(jìn)程還在運(yùn)行遗淳,那么那些子進(jìn)程將成為孤兒進(jìn)程。孤兒進(jìn)程將被init進(jìn)程(進(jìn)程號(hào)為1)所收養(yǎng)令哟,并由init進(jìn)程對(duì)它們完成狀態(tài)收集工作恼琼。

僵尸進(jìn)程:一個(gè)進(jìn)程使用fork創(chuàng)建子進(jìn)程,如果子進(jìn)程退出屏富,而父進(jìn)程并沒有調(diào)用wait或waitpid獲取子進(jìn)程的狀態(tài)信息晴竞,那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中。這種進(jìn)程稱之為僵死進(jìn)程狠半。

unix提供了一種機(jī)制可以保證只要父進(jìn)程想知道子進(jìn)程結(jié)束時(shí)的狀態(tài)信息噩死, 就可以得到颤难。這種機(jī)制就是: 在每個(gè)進(jìn)程退出的時(shí)候,內(nèi)核釋放該進(jìn)程所有的資源,包括打開的文件,占用的內(nèi)存等。 但是仍然為其保留一定的信息(包括進(jìn)程號(hào)the process ID,退出狀態(tài)the termination status of the process,運(yùn)行時(shí)間the amount of CPU time taken by the process等)已维。直到父進(jìn)程通過wait / waitpid來取時(shí)才釋放行嗤。 但這樣就導(dǎo)致了問題,如果進(jìn)程不調(diào)用wait / waitpid的話垛耳, 那么保留的那段信息就不會(huì)釋放栅屏,其進(jìn)程號(hào)就會(huì)一直被占用,但是系統(tǒng)所能使用的進(jìn)程號(hào)是有限的堂鲜,如果大量的產(chǎn)生僵死進(jìn)程栈雳,將因?yàn)闆]有可用的進(jìn)程號(hào)而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程。此即為僵尸進(jìn)程的危害缔莲,應(yīng)當(dāng)避免哥纫。

1. linux下的僵尸進(jìn)程處理SIGCHLD

首先內(nèi)核會(huì)釋放終止進(jìn)程(調(diào)用了exit系統(tǒng)調(diào)用)所使用的所有存儲(chǔ)區(qū),關(guān)閉所有打開的文件等痴奏,但內(nèi)核為每一個(gè)終止子進(jìn)程保存了一定量的信息蛀骇。這些信息至少包括進(jìn)程ID,進(jìn)程的終止?fàn)顟B(tài)抛虫,以及該進(jìn)程使用的CPU時(shí)間松靡,所以當(dāng)終止子進(jìn)程的父進(jìn)程調(diào)用wait或waitpid時(shí)就可以得到這些信息。
而僵尸進(jìn)程就是指:一個(gè)進(jìn)程執(zhí)行了exit系統(tǒng)調(diào)用退出建椰,而其父進(jìn)程并沒有為它收尸(調(diào)用wait或waitpid來獲得它的結(jié)束狀態(tài))的進(jìn)程。
任何一個(gè)子進(jìn)程(init除外)在exit后并非馬上就消失岛马,而是留下一個(gè)稱外僵尸進(jìn)程的數(shù)據(jù)結(jié)構(gòu)棉姐,等待父進(jìn)程處理。這是每個(gè)子進(jìn)程都必需經(jīng)歷的階段啦逆。另外子進(jìn)程退出的時(shí)候會(huì)向其父進(jìn)程發(fā)送一個(gè)SIGCHLD信號(hào)伞矩。

設(shè)置僵死狀態(tài)的目的是維護(hù)子進(jìn)程的信息,以便父進(jìn)程在以后某個(gè)時(shí)候獲取夏志。這些信息至少包括進(jìn)程ID乃坤,進(jìn)程的終止?fàn)顟B(tài),以及該進(jìn)程使用的CPU時(shí)間沟蔑,所以當(dāng)終止子進(jìn)程的父進(jìn)程調(diào)用wait或waitpid時(shí)就可以得到這些信息湿诊。如果一個(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))食棕。

如何避免僵尸進(jìn)程

  1. 通過signal(SIGCHLD, SIG_IGN)通知內(nèi)核對(duì)子進(jìn)程的結(jié)束不關(guān)心朗和,由內(nèi)核回收错沽。如果不想讓父進(jìn)程掛起,可以在父進(jìn)程中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父進(jìn)程忽略SIGCHLD信號(hào)眶拉,該信號(hào)是子進(jìn)程退出的時(shí)候向父進(jìn)程發(fā)送的千埃。
  2. 父進(jìn)程調(diào)用wait/waitpid等函數(shù)等待子進(jìn)程結(jié)束,如果尚無子進(jìn)程退出wait會(huì)導(dǎo)致父進(jìn)程阻塞忆植。waitpid可以通過傳遞WNOHANG使父進(jìn)程不阻塞立即返回镰禾。
  3. 如果父進(jìn)程很忙可以用signal注冊(cè)信號(hào)處理函數(shù),在信號(hào)處理函數(shù)調(diào)用wait/waitpid等待子進(jìn)程退出唱逢。
  4. 通過兩次調(diào)用fork吴侦。父進(jìn)程首先調(diào)用fork創(chuàng)建一個(gè)子進(jìn)程然后waitpid等待子進(jìn)程退出,子進(jìn)程再fork一個(gè)孫進(jìn)程后退出坞古。這樣子進(jìn)程退出后會(huì)被父進(jìn)程等待回收备韧,而對(duì)于孫子進(jìn)程其父進(jìn)程已經(jīng)退出所以孫進(jìn)程成為一個(gè)孤兒進(jìn)程,孤兒進(jìn)程由init進(jìn)程接管痪枫,孫進(jìn)程結(jié)束后织堂,init會(huì)等待回收。

第一種方法忽略SIGCHLD信號(hào)奶陈,這常用于并發(fā)服務(wù)器的性能的一個(gè)技巧因?yàn)椴l(fā)服務(wù)器常常fork很多子進(jìn)程易阳,子進(jìn)程終結(jié)之后需要服務(wù)器進(jìn)程去wait清理資源。如果將此信號(hào)的處理方式設(shè)為忽略吃粒,可讓內(nèi)核把僵尸子進(jìn)程轉(zhuǎn)交給init進(jìn)程去處理潦俺,省去了大量僵尸進(jìn)程占用系統(tǒng)資源。

2. 僵尸進(jìn)程處理辦法

(1) wait()函數(shù)

#include <sys/types.h> 
#include <sys/wait.h>
pid_t wait(int *status);

進(jìn)程一旦調(diào)用了wait徐勃,就立即阻塞自己事示,由wait自動(dòng)分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出,如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程僻肖,wait就會(huì)收集這個(gè)子進(jìn)程的信息肖爵,并把它徹底銷毀后返回;如果沒有找到這樣一個(gè)子進(jìn)程臀脏,wait就會(huì)一直阻塞在這里劝堪,直到有一個(gè)出現(xiàn)為止。
參數(shù)status用來保存被收集進(jìn)程退出時(shí)的一些狀態(tài)揉稚,它是一個(gè)指向int類型的指針秒啦。但如果我們對(duì)這個(gè)子進(jìn)程是如何死掉的毫不在意,只想把這個(gè)僵尸進(jìn)程消滅掉窃植,(事實(shí)上絕大多數(shù)情況下帝蒿,我們都會(huì)這樣想),我們就可以設(shè)定這個(gè)參數(shù)為NULL巷怜,就象下面這樣:

pid = wait(NULL);

如果成功葛超,wait會(huì)返回被收集的子進(jìn)程的進(jìn)程ID暴氏,如果調(diào)用進(jìn)程沒有子進(jìn)程,調(diào)用就會(huì)失敗绣张,此時(shí)wait返回-1答渔,同時(shí)errno被置為ECHILD

wait系統(tǒng)調(diào)用會(huì)使父進(jìn)程暫停執(zhí)行侥涵,直到它的一個(gè)子進(jìn)程結(jié)束為止沼撕。

  • 返回的是子進(jìn)程的PID,它通常是結(jié)束的子進(jìn)程
  • 狀態(tài)信息允許父進(jìn)程判定子進(jìn)程的退出狀態(tài)芜飘,即從子進(jìn)程的main函數(shù)返回的值或子進(jìn)程中exit語句的退出碼务豺。
  • 如果status不是一個(gè)空指針,狀態(tài)信息將被寫入它指向的位置

可以上述的一些宏判斷子進(jìn)程的退出情況

(2) waitpid()函數(shù)

#include <sys/types.h> 
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

參數(shù)
status:如果不是空嗦明,會(huì)把狀態(tài)信息寫到它指向的位置笼沥,與wait一樣
options:允許改變waitpid的行為,最有用的一個(gè)選項(xiàng)是WNOHANG,它的作用是防止waitpid把調(diào)用者的執(zhí)行掛起

WNOHANG return immediately if no child has exited.
WUNTRACED also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is provided even if this option is not specified.
WCONTINUED (since Linux 2.6.10)
also return if a stopped child has been resumed by delivery of SIGCONT.

返回值:
如果成功返回等待子進(jìn)程的ID娶牌,失敗返回-1

對(duì)于waitpid的pid參數(shù)的解釋與其值有關(guān):

  • pid == -1 等待任一子進(jìn)程奔浅。于是在這一功能方面waitpid與wait等效。
  • pid > 0 等待其進(jìn)程ID與pid相等的子進(jìn)程诗良。
  • pid == 0 等待其組ID等于調(diào)用進(jìn)程的組ID的任一子進(jìn)程汹桦。換句話說是與調(diào)用者進(jìn)程同在一個(gè)組的進(jìn)程。
  • pid < -1 等待其組ID等于pid的絕對(duì)值的任一子進(jìn)程

wait與waitpid區(qū)別:

  • 在一個(gè)子進(jìn)程終止前鉴裹, wait 使其調(diào)用者阻塞舞骆,而waitpid 有一選擇項(xiàng),可使調(diào)用者不阻塞壹罚。
  • waitpid并不等待第一個(gè)終止的子進(jìn)程—它有若干個(gè)選擇項(xiàng)葛作,可以控制它所等待的特定進(jìn)程。
  • 實(shí)際上wait函數(shù)是waitpid函數(shù)的一個(gè)特例猖凛。waitpid(-1, &status, 0) ;

示例

如以下代碼會(huì)創(chuàng)建100個(gè)子進(jìn)程,但是父進(jìn)程并未等待它們結(jié)束绪穆,所以在父進(jìn)程退出前會(huì)有100個(gè)僵尸進(jìn)程辨泳。

#include <stdio.h>  
#include <unistd.h>  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}

其中一個(gè)解決方法即是編寫一個(gè)SIGCHLD信號(hào)處理程序來調(diào)用wait/waitpid來等待子進(jìn)程返回。

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
   
void wait4children(int signo) {  
   
  int status;  
  wait(&status);  
   
}  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  signal(SIGCHLD, wait4children);  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}  

但是通過運(yùn)行程序發(fā)現(xiàn)還是會(huì)有僵尸進(jìn)程玖院,而且每次僵尸進(jìn)程的數(shù)量都不定菠红。這是為什么呢?其實(shí)主要是因?yàn)長inux的信號(hào)機(jī)制是不排隊(duì)的难菌,假如在某一時(shí)間段多個(gè)子進(jìn)程退出后都會(huì)發(fā)出SIGCHLD信號(hào)试溯,但父進(jìn)程來不及一個(gè)一個(gè)地響應(yīng),所以最后父進(jìn)程實(shí)際上只執(zhí)行了一次信號(hào)處理函數(shù)郊酒。但執(zhí)行一次信號(hào)處理函數(shù)只等待一個(gè)子進(jìn)程退出遇绞,所以最后會(huì)有一些子進(jìn)程依然是僵尸進(jìn)程键袱。

雖然這樣但是有一點(diǎn)是明了的,就是收到SIGCHLD必然有子進(jìn)程退出摹闽,而我們可以在信號(hào)處理函數(shù)里循環(huán)調(diào)用waitpid函數(shù)來等待所有的退出的子進(jìn)程蹄咖。至于為什么不用wait,主要原因是在wait在清理完所有僵尸進(jìn)程后再次等待會(huì)阻塞付鹿。

所以最佳方案如下:

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
   
void wait4children(int signo) {  
  int status;  
  while(waitpid(-1, &status, WNOHANG) > 0);  
}  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  signal(SIGCHLD, wait4children);  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}  

這里使用waitpid而不是使用wait的原因在于:我們?cè)谝粋€(gè)循環(huán)內(nèi)調(diào)用waitpid澜汤,以獲取所有已終止子進(jìn)程的狀態(tài)。我們必須指定WNOHANG選項(xiàng)舵匾,它告訴waitpid在有尚未終止的子進(jìn)程在運(yùn)行時(shí)不要阻塞俊抵。我們不能在循環(huán)內(nèi)調(diào)用wait,因?yàn)闆]有辦法防止wait在正運(yùn)行的子進(jìn)程尚有未終止時(shí)阻塞坐梯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末徽诲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子烛缔,更是在濱河造成了極大的恐慌馏段,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件践瓷,死亡現(xiàn)場(chǎng)離奇詭異院喜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晕翠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門喷舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人淋肾,你說我怎么就攤上這事硫麻。” “怎么了樊卓?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵拿愧,是天一觀的道長。 經(jīng)常有香客問我碌尔,道長浇辜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任唾戚,我火速辦了婚禮柳洋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叹坦。我一直安慰自己熊镣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绪囱,像睡著了一般测蹲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毕箍,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天弛房,我揣著相機(jī)與錄音,去河邊找鬼而柑。 笑死文捶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的媒咳。 我是一名探鬼主播粹排,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼涩澡!你這毒婦竟也來了顽耳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤妙同,失蹤者是張志新(化名)和其女友劉穎射富,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粥帚,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胰耗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芒涡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柴灯。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖费尽,靈堂內(nèi)的尸體忽然破棺而出赠群,到底是詐尸還是另有隱情,我是刑警寧澤旱幼,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布查描,位于F島的核電站,受9級(jí)特大地震影響柏卤,放射性物質(zhì)發(fā)生泄漏叹誉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一闷旧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钧唐,春花似錦忙灼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酸舍。三九已至,卻和暖如春里初,著一層夾襖步出監(jiān)牢的瞬間啃勉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工双妨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淮阐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓刁品,卻偏偏與公主長得像泣特,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挑随,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 基本概念 我們知道在unix/linux中状您,正常情況下,子進(jìn)程是通過父進(jìn)程創(chuàng)建的兜挨,子進(jìn)程在創(chuàng)建新的進(jìn)程膏孟。子進(jìn)程的結(jié)...
    01_小小魚_01閱讀 303評(píng)論 0 0
  • 孤兒進(jìn)程與僵尸進(jìn)程[總結(jié)] 1、前言 之前在看《unix環(huán)境高級(jí)編程》第八章進(jìn)程時(shí)候拌汇,提到孤兒進(jìn)程和僵尸進(jìn)程柒桑,一直...
    徐德東閱讀 245評(píng)論 0 0
  • 兩個(gè)月前滿懷期待開心回來,如今帶著一身疲憊和心痛離開担猛。剛在候車室眼淚又止不住的流幕垦,看著熙熙攘攘的人群心里更加落寞。...
    筿筿閱讀 627評(píng)論 4 4
  • 引子 “星辰傅联,你性格真好先改,好喜歡你啊。...
    阡陌佳人閱讀 144評(píng)論 0 1
  • 七月雨蒸走,悠長蒼天不垂憐洪水卷走半生的積蓄房子沒了家沒了一切都沒了淚水早已流干心在泣血到底為什么啊今夜仇奶,我的孩子將睡...
    瑜伽散人閱讀 269評(píng)論 3 9