參考博客 : http://www.cnblogs.com/Anker/p/3271773.html
一:僵尸進(jìn)程(有害
進(jìn)程:一個(gè)進(jìn)程使用fork創(chuàng)建子進(jìn)程,如果子進(jìn)程退出,而父進(jìn)程并沒(méi)有調(diào)用wait或waitpid獲取子進(jìn)程的狀態(tài)信息,那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中。這種進(jìn)程稱(chēng)之為僵死進(jìn)程。詳解如下:
們知道在unix/linux中,正常情況下子進(jìn)程是通過(guò)父進(jìn)程創(chuàng)建的,子進(jìn)程在創(chuàng)建新的進(jìn)程。子進(jìn)程的結(jié)束和父進(jìn)程的運(yùn)行是一個(gè)異步過(guò)程,即父進(jìn)程永遠(yuǎn)無(wú)法預(yù)測(cè)子進(jìn)程到底什么時(shí)候結(jié)束,如果子進(jìn)程一結(jié)束就立刻回收其全部資源,那么在父進(jìn)程內(nèi)將無(wú)法獲取子進(jìn)程的狀態(tài)信息。
因此,UNⅨ提供了一種機(jī)制可以保證父進(jìn)程可以在任意時(shí)刻獲取子進(jìn)程結(jié)束時(shí)的狀態(tài)信息:
1、在每個(gè)進(jìn)程退出的時(shí)候,內(nèi)核釋放該進(jìn)程所有的資源,包括打開(kāi)的文件,占用的內(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等)
2、直到父進(jìn)程通過(guò)wait / waitpid來(lái)取時(shí)才釋放. 但這樣就導(dǎo)致了問(wèn)題,如果進(jìn)程不調(diào)用wait / waitpid的話,那么保留的那段信息就不會(huì)釋放,其進(jìn)程號(hào)就會(huì)一直被占用,但是系統(tǒng)所能使用的進(jìn)程號(hào)是有限的,如果大量的產(chǎn)生僵死進(jìn)程,將因?yàn)闆](méi)有可用的進(jìn)程號(hào)而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程. 此即為僵尸進(jìn)程的危害,應(yīng)當(dāng)避免。
任何一個(gè)子進(jìn)程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個(gè)稱(chēng)為僵尸進(jìn)程(Zombie)的數(shù)據(jù)結(jié)構(gòu),等待父進(jìn)程處理。這是每個(gè)子進(jìn)程在結(jié)束時(shí)都要經(jīng)過(guò)的階段。如果子進(jìn)程在exit()之后,父進(jìn)程沒(méi)有來(lái)得及處理,這時(shí)用ps命令就能看到子進(jìn)程的狀態(tài)是“Z”。如果父進(jìn)程能及時(shí) 處理,可能用ps命令就來(lái)不及看到子進(jìn)程的僵尸狀態(tài),但這并不等于子進(jìn)程不經(jīng)過(guò)僵尸狀態(tài)。 如果父進(jìn)程在子進(jìn)程結(jié)束之前退出,則子進(jìn)程將由init接管。init將會(huì)以父進(jìn)程的身份對(duì)僵尸狀態(tài)的子進(jìn)程進(jìn)行處理。
二:孤兒進(jìn)程(無(wú)害)
孤兒進(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)程是沒(méi)有父進(jìn)程的進(jìn)程,孤兒進(jìn)程這個(gè)重任就落到了init進(jìn)程身上,init進(jìn)程就好像是一個(gè)民政局,專(zhuān)門(mén)負(fù)責(zé)處理孤兒進(jìn)程的善后工作。每當(dāng)出現(xiàn)一個(gè)孤兒進(jìn)程的時(shí)候,內(nèi)核就把孤 兒進(jìn)程的父進(jìn)程設(shè)置為init,而init進(jìn)程會(huì)循環(huán)地wait()它的已經(jīng)退出的子進(jìn)程。這樣,當(dāng)一個(gè)孤兒進(jìn)程凄涼地結(jié)束了其生命周期的時(shí)候,init進(jìn)程就會(huì)代表黨和政府出面處理它的一切善后工作。因此孤兒進(jìn)程并不會(huì)有什么危害。
我們來(lái)測(cè)試一下(創(chuàng)建完子進(jìn)程后,主進(jìn)程所在的這個(gè)腳本就退出了,當(dāng)父進(jìn)程先于子進(jìn)程結(jié)束時(shí),子進(jìn)程會(huì)被init收養(yǎng),成為孤兒進(jìn)程,而非僵尸進(jìn)程),文件內(nèi)容
import os
import sys
import time
pid = os.getpid()
ppid = os.getppid()
print 'im father', 'pid', pid, 'ppid', ppid
pid = os.fork()
#執(zhí)行pid=os.fork()則會(huì)生成一個(gè)子進(jìn)程
#返回值pid有兩種值:
# 如果返回的pid值為0,表示在子進(jìn)程當(dāng)中
# 如果返回的pid值>0,表示在父進(jìn)程當(dāng)中
if pid > 0:
print 'father died..'
sys.exit(0)
# 保證主線程退出完畢
time.sleep(1)
print 'im child', os.getpid(), os.getppid()
執(zhí)行文件,輸出結(jié)果:
im father pid 32515 ppid 32015
father died..
im child 32516 1
子進(jìn)程已經(jīng)被pid為1的init進(jìn)程接收了,所以僵尸進(jìn)程在這種情況下是不存在的,存在只有孤兒進(jìn)程而已,孤兒進(jìn)程聲明周期結(jié)束自然會(huì)被init來(lái)銷(xiāo)毀。
三:僵尸進(jìn)程危害場(chǎng)景:
例如有個(gè)進(jìn)程,它定期的產(chǎn) 生一個(gè)子進(jìn)程,這個(gè)子進(jìn)程需要做的事情很少,做完它該做的事情之后就退出了,因此這個(gè)子進(jìn)程的生命周期很短,但是,父進(jìn)程只管生成新的子進(jìn)程,至于子進(jìn)程 退出之后的事情,則一概不聞不問(wèn),這樣,系統(tǒng)運(yùn)行上一段時(shí)間之后,系統(tǒng)中就會(huì)存在很多的僵死進(jìn)程,倘若用ps命令查看的話,就會(huì)看到很多狀態(tài)為Z的進(jìn)程。 嚴(yán)格地來(lái)說(shuō),僵死進(jìn)程并不是問(wèn)題的根源,罪魁禍?zhǔn)资钱a(chǎn)生出大量僵死進(jìn)程的那個(gè)父進(jìn)程。因此,當(dāng)我們尋求如何消滅系統(tǒng)中大量的僵死進(jìn)程時(shí),答案就是把產(chǎn)生大 量僵死進(jìn)程的那個(gè)元兇槍斃掉(也就是通過(guò)kill發(fā)送SIGTERM或者SIGKILL信號(hào)啦)。槍斃了元兇進(jìn)程之后,它產(chǎn)生的僵死進(jìn)程就變成了孤兒進(jìn) 程,這些孤兒進(jìn)程會(huì)被init進(jìn)程接管,init進(jìn)程會(huì)wait()這些孤兒進(jìn)程,釋放它們占用的系統(tǒng)進(jìn)程表中的資源,這樣,這些已經(jīng)僵死的孤兒進(jìn)程 就能瞑目而去了。
四:測(cè)試
1、產(chǎn)生僵尸進(jìn)程的程序test.py內(nèi)容如下
#coding:utf-8
from multiprocessing import Process
import time,os
def run():
print('子',os.getpid())
if __name__ == '__main__':
p=Process(target=run)
p.start()
print('主',os.getpid())
time.sleep(1000)
2、在unix或linux系統(tǒng)上執(zhí)行
[root@vm172-31-0-19 ~]# python3 test.py &
[1] 18652
[root@vm172-31-0-19 ~]# 主 18652
子 18653
[root@vm172-31-0-19 ~]# ps aux |grep Z
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 18653 0.0 0.0 0 0 pts/0 Z 20:02 0:00 [python3] <defunct> #出現(xiàn)僵尸進(jìn)程
root 18656 0.0 0.0 112648 952 pts/0 S+ 20:02 0:00 grep --color=auto Z
[root@vm172-31-0-19 ~]# top #執(zhí)行top命令發(fā)現(xiàn)1zombie
top - 20:03:42 up 31 min, 3 users, load average: 0.01, 0.06, 0.12
Tasks: 93 total, 2 running, 90 sleeping, 0 stopped, 1 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016884 total, 97184 free, 70848 used, 848852 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 782540 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
root 20 0 29788 1256 988 S 0.3 0.1 0:01.50 elfin
3、
等待父進(jìn)程正常結(jié)束后會(huì)調(diào)用wait/waitpid去回收僵尸進(jìn)程
但如果父進(jìn)程是一個(gè)死循環(huán),永遠(yuǎn)不會(huì)結(jié)束,那么該僵尸進(jìn)程就會(huì)一直存在,僵尸進(jìn)程過(guò)多,就是有害的
解決方法一:殺死父進(jìn)程
解決方法二:對(duì)開(kāi)啟的子進(jìn)程應(yīng)該記得使用join,join會(huì)回收僵尸進(jìn)程
參考python2源碼注釋
class Process(object):
def join(self, timeout=None):
'''
Wait until child process terminates
'''
assert self._parent_pid == os.getpid(), 'can only join a child process'
assert self._popen is not None, 'can only join a started process'
res = self._popen.wait(timeout)
if res is not None:
_current_process._children.discard(self)
in方法中調(diào)用了wait,告訴系統(tǒng)釋放僵尸進(jìn)程。discard為從自己的children中剔除
解決方法三:http://blog.csdn.net/u010571844/article/details/50419798