python僵尸進程產(chǎn)生的原因
在 unix 或 unix-like 的系統(tǒng)中,當(dāng)一個子進程退出后,它就會變成一個僵尸進程,如果父進程沒有通過 wait 系統(tǒng)調(diào)用來讀取這個子進程的退出狀態(tài)的話,這個子進程就會一直維持僵尸進程狀態(tài)。
Zombie process - Wikipedia 中是這樣描述的:
On Unix and Unix-like computer operating systems, a zombie process or defunct process is a process that has completed execution (via the exit system call) but still has an entry in the process table: it is a process in the "Terminated state". This occurs for child processes, where the entry is still needed to allow the parent process to read its child's exit status: once the exit status is read via the wait system call, the zombie's entry is removed from the process table and it is said to be "reaped". A child process always first becomes a zombie before being removed from the resource table. In most cases, under normal system operation zombies are immediately waited on by their parent and then reaped by the system – processes that stay zombies for a long time are generally an error and cause a resource leak.
并且僵尸進程無法通過 kill 命令來清除。
本文將探討如何手動制造一個僵尸進程以及清除僵尸進程的辦法。
手動制造一個僵尸進程
為了便于后面講解清除僵尸進程的方法,我們使用日常開發(fā)中經(jīng)常使用的 multiprocessing 模塊來制造僵尸進程(準(zhǔn)確的來說是制造一個長時間維持僵尸進程狀態(tài)的子進程):
$ cat test_a.py
from multiprocessing import Process, current_process
import logging
import os
import time
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)-15s - %(levelname)s - %(message)s'
)
def run():
logging.info('exit child process %s', current_process().pid)
os._exit(3)
p = Process(target=run)
p.start()
time.sleep(100)
測試:
$ python test_a.py & [1] 10091 $ 2017-07-20 21:28:14,792 - INFO - exit child process 10106 $ ps aux |grep 10106 mozillazg 10126 0.0 0.0 2434836 740 s006 R+ 0:00.00 grep 10106 mozillazg 10106 0.0 0.0 0 0 s006 Z 0:00.00 (Python)
可以看到,子進程 10091 變成了僵尸進程。
既然已經(jīng)可以控制僵尸進程的產(chǎn)生了,那我們就可以進入下一步如何清除僵尸進程了。
清除僵尸進程有兩種方法:
•第一種方法就是結(jié)束父進程。當(dāng)父進程退出的時候僵尸進程隨后也會被清除。
• 第二種方法就是通過 wait 調(diào)用來讀取子進程退出狀態(tài)。我們可以通過處理 SIGCHLD 信號,在處理程序中調(diào)用 wait 系統(tǒng)調(diào)用來清除僵尸進程。
處理 SIGCHLD 信號
子進程退出時系統(tǒng)會向父進程發(fā)送 SIGCHLD 信號,父進程可以通過注冊 SIGCHLD 信號處理程序,在信號處理程序中調(diào)用 wait
系統(tǒng)調(diào)用來清理僵尸進程。 $ cat test_b.py
import errno
from multiprocessing import Process, current_process
import logging
import os
import signal
import time
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)-15s - %(levelname)s - %(message)s'
)
def run():
exitcode = 3
logging.info('exit child process %s with exitcode %s',
current_process().pid, exitcode)
os._exit(exitcode)
def wait_child(signum, frame):
logging.info('receive SIGCHLD')
try:
while True:
# -1 表示任意子進程
# os.WNOHANG 表示如果沒有可用的需要 wait 退出狀態(tài)的子進程,立即返回不阻塞
cpid, status = os.waitpid(-1, os.WNOHANG)
if cpid == 0:
logging.info('no child process was immediately available')
break
exitcode = status >> 8
logging.info('child process %s exit with exitcode %s', cpid, exitcode)
except OSError as e:
if e.errno == errno.ECHILD:
logging.error('current process has no existing unwaited-for child processes.')
else:
raise
logging.info('handle SIGCHLD end')
signal.signal(signal.SIGCHLD, wait_child)
p = Process(target=run)
p.start()
while True:
time.sleep(100)
效果:
$ python test_b.py & [1] 10159 $ 2017-07-20 21:28:56,085 - INFO - exit child process 10174 with exitcode 3 2017-07-20 21:28:56,088 - INFO - receive SIGCHLD 2017-07-20 21:28:56,089 - INFO - child process 10174 exit with exitcode 3 2017-07-20 21:28:56,090 - ERROR - current process has no existing unwaited-for child processes. 2017-07-20 21:28:56,090 - INFO - handle SIGCHLD end $ ps aux |grep 10174 mozillazg 10194 0.0 0.0 2432788 556 s006 R+ 0:00.00 grep 10174
可以看到,子進程退出變成僵尸進程后,系統(tǒng)給父進程發(fā)送了 SIGCHLD 信號,我們在 SIGCHLD 信號的處理程序中通過 os.waitpid 調(diào)用 wait 系統(tǒng)調(diào)用后阻止了子進程一直處于僵尸進程狀態(tài),從而實現(xiàn)了清除僵尸進程的效果。
相關(guān)文章
Python Collections強大的數(shù)據(jù)結(jié)構(gòu)工具使用實例探索
這篇文章主要介紹了Python Collections強大的數(shù)據(jù)結(jié)構(gòu)工具的使用實例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01
基于Python實現(xiàn)一鍵獲取電腦瀏覽器的賬號密碼
發(fā)現(xiàn)很多人在學(xué)校圖書館喜歡用電腦占座,而且出去的時候經(jīng)常不鎖屏,為了讓大家養(yǎng)成良好的習(xí)慣,本文將分享一個小程序,可以快速獲取你存儲在電腦瀏覽器中的所有賬號和密碼,感興趣的可以了解一下2022-05-05
Python爬蟲實戰(zhàn)演練之采集糗事百科段子數(shù)據(jù)
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Python采集糗事百科段子的數(shù)據(jù),大家可以在過程中查缺補漏,提升水平2021-10-10
機器學(xué)習(xí)經(jīng)典算法-logistic回歸代碼詳解
這篇文章主要介紹了機器學(xué)習(xí)經(jīng)典算法-logistic回歸代碼詳解,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
Python itertools.product方法代碼實例
這篇文章主要介紹了Python itertools.product方法代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03

