在UNIX里,除了進(jìn)程0(即PID=0的交換進(jìn)程,Swapper Process)以外的所有進(jìn)程都是由其他進(jìn)程使用系統(tǒng)調(diào)用fork創(chuàng)建的,這里調(diào)用fork創(chuàng)建新進(jìn)程的進(jìn)程即為父進(jìn)程,而相對(duì)應(yīng)的為其創(chuàng)建出的進(jìn)程則為子進(jìn)程,因而除了進(jìn)程0以外的進(jìn)程都只有一個(gè)父進(jìn)程,但一個(gè)進(jìn)程可以有多個(gè)子進(jìn)程。
操作系統(tǒng)內(nèi)核以進(jìn)程標(biāo)識(shí)符(Process Identifier,即PID)來識(shí)別進(jìn)程。進(jìn)程0是系統(tǒng)引導(dǎo)時(shí)創(chuàng)建的一個(gè)特殊進(jìn)程,在其調(diào)用fork創(chuàng)建出一個(gè)子進(jìn)程(即PID=1的進(jìn)程1,又稱init)后,進(jìn)程0就轉(zhuǎn)為交換進(jìn)程(有時(shí)也被稱為空閑進(jìn)程),而進(jìn)程1(init進(jìn)程)就是系統(tǒng)里其他所有進(jìn)程的祖先。
僵尸進(jìn)程與孤兒進(jìn)程
當(dāng)一個(gè)子進(jìn)程結(jié)束運(yùn)行(一般是調(diào)用exit、運(yùn)行時(shí)發(fā)生致命錯(cuò)誤或收到終止信號(hào)所導(dǎo)致)時(shí),子進(jìn)程的退出狀態(tài)(返回值)會(huì)回報(bào)給操作系統(tǒng),系統(tǒng)則以SIGCHLD信號(hào)將子進(jìn)程被結(jié)束的事件告知父進(jìn)程,此時(shí)子進(jìn)程的進(jìn)程控制塊(PCB)仍駐留在內(nèi)存中。一般來說,收到SIGCHLD后,父進(jìn)程會(huì)使用wait系統(tǒng)調(diào)用以獲取子進(jìn)程的退出狀態(tài),然后內(nèi)核就可以從內(nèi)存中釋放已結(jié)束的子進(jìn)程的PCB;而如若父進(jìn)程沒有這么做的話,子進(jìn)程的PCB就會(huì)一直駐留在內(nèi)存中,也即成為僵尸進(jìn)程。
孤兒進(jìn)程則是指父進(jìn)程結(jié)束后仍在運(yùn)行的子進(jìn)程。在類UNIX系統(tǒng)中,孤兒進(jìn)程一般會(huì)被init進(jìn)程所“收養(yǎng)”,成為init的子進(jìn)程。
為避免產(chǎn)生僵尸進(jìn)程,實(shí)際應(yīng)用中一般采取的方式是:
將父進(jìn)程中對(duì)SIGCHLD信號(hào)的處理函數(shù)設(shè)為SIG_IGN(忽略信號(hào));
fork兩次并殺死一級(jí)子進(jìn)程,令二級(jí)子進(jìn)程成為孤兒進(jìn)程而被init所“收養(yǎng)”、清理。
Linux
在Linux內(nèi)核中,進(jìn)程和POSIX線程有著相當(dāng)微小的區(qū)別,父進(jìn)程的定義也與UNIX不盡相同。Linux有兩種父進(jìn)程,分別稱為(形式)父進(jìn)程與實(shí)際父進(jìn)程,對(duì)于一個(gè)子進(jìn)程來說,其父進(jìn)程是在子進(jìn)程結(jié)束時(shí)收取SIGCHLD信號(hào)的進(jìn)程,而實(shí)際父進(jìn)程則是在多線程環(huán)境里實(shí)際創(chuàng)建該子進(jìn)程的進(jìn)程。對(duì)于普通進(jìn)程來說,父進(jìn)程與實(shí)際父進(jìn)程是同一個(gè)進(jìn)程,但對(duì)于一個(gè)以進(jìn)程形式存在的POSIX線程,父進(jìn)程和實(shí)際父進(jìn)程可能是不一樣的。
子進(jìn)程
在計(jì)算機(jī)領(lǐng)域中,子進(jìn)程為由另外一個(gè)進(jìn)程(對(duì)應(yīng)稱之為父進(jìn)程)所創(chuàng)建的進(jìn)程。子進(jìn)程繼承了父進(jìn)程的大部分屬性,例如文件描述符。
產(chǎn)生
在Unix中,子進(jìn)程通常為系統(tǒng)調(diào)用fork的產(chǎn)物。在此情況下,子進(jìn)程一開始就是父進(jìn)程的副本,而在這之后,根據(jù)具體需要,子進(jìn)程可以借助exec調(diào)用來鏈?zhǔn)郊虞d另一程序。
與父進(jìn)程的關(guān)系
一個(gè)進(jìn)程可能下屬多個(gè)子進(jìn)程,但最多只能有1個(gè)父進(jìn)程,而若某一進(jìn)程沒有父進(jìn)程,則可知該進(jìn)程很可能由內(nèi)核直接生成。在Unix與類Unix系統(tǒng)中,進(jìn)程ID為1的進(jìn)程(即init進(jìn)程)是在系統(tǒng)引導(dǎo)階段由內(nèi)核直接創(chuàng)建的,且不會(huì)在系統(tǒng)運(yùn)行過程中終止執(zhí)行(可參見Linux啟動(dòng)流程);而對(duì)于其他無父進(jìn)程的進(jìn)程,則可能是為在用戶空間完成各種后臺(tái)任務(wù)而執(zhí)行的。
當(dāng)某一子進(jìn)程結(jié)束、中斷或恢復(fù)執(zhí)行時(shí),內(nèi)核會(huì)發(fā)送SIGCHLD信號(hào)予其父進(jìn)程。在默認(rèn)情況下,父進(jìn)程會(huì)以SIG_IGN函數(shù)忽略之。
“孤兒進(jìn)程”與“僵尸進(jìn)程”
在對(duì)應(yīng)的父進(jìn)程結(jié)束執(zhí)行后,進(jìn)程就會(huì)變成孤兒進(jìn)程,但之后會(huì)立即由init進(jìn)程“收養(yǎng)”為其子進(jìn)程。
某一子進(jìn)程終止執(zhí)行后,若其父進(jìn)程未提前調(diào)用wait,則內(nèi)核會(huì)持續(xù)保留子進(jìn)程的退出狀態(tài)等信息,以使父進(jìn)程可以wait獲取之。而因?yàn)樵谶@種情況下,子進(jìn)程雖已終止,但仍在消耗系統(tǒng)資源,所以其亦稱僵尸進(jìn)程。wait常于SIGCHLD信號(hào)的處理函數(shù)中調(diào)用。
解決與預(yù)防
在POSIX.1-2001標(biāo)準(zhǔn)規(guī)定中,父進(jìn)程可將SIGCHLD的處理函數(shù)設(shè)為SIG_IGN(亦為默認(rèn)設(shè)定),或?yàn)镾IGCHLD設(shè)定SA_NOCLDWAIT標(biāo)記,以使內(nèi)核可以自動(dòng)回收已終止的子進(jìn)程的資源。自Linux 2.6與FreeBSD 5.0起,兩種內(nèi)核皆支持了這兩種方式。但是,在忽略SIGCHLD信號(hào)的問題上,由于System V與BSD由來已久的差異,若要回收派生出的子進(jìn)程的資源,調(diào)用wait仍是最便捷的方式。
我們的公共號(hào)
更多關(guān)于云服務(wù)器,域名注冊(cè),虛擬主機(jī)的問題,請(qǐng)?jiān)L問西部數(shù)碼官網(wǎng):www.ps-sw.cn