- 1.背景
- 2.思路
- 3.僵尸进程回收方法
- 4.调用方法
在Python中使用 p = subprocess.Popen 执行 shell命令时,如果不使用 p.wait() ,就会产生僵尸进程,对于僵尸进程可以采用 os.wait(),os.waitpid()等方式回收,但是os.wait()是阻塞的,所以一般采用os.waitpid() 回收指定僵尸进程。
2.思路0.在主进程下启动一个线程,主管僵尸进程回收工作 1.先获取主进程下的所有子进程列表 2.再获取linux系统的所有僵尸进程列表 3.列表取交集,即是当前主进程下的僵尸子进程 4.然后对主进程下的僵尸子进程计入字典,key是僵尸子进程pid,value是计入子进程的时间戳 5.计算字典记录中僵尸子进程存活时间 6.如果超过阈值,则主动回收该子进程 7.循环回收3.僵尸进程回收方法
# -*- coding: utf-8 -*-
"""
Author: tanggaomeng
date: 2021/11/20
Description:
回收僵尸子进程
1.只能回收主进程下面的僵尸子进程,对孙子、重孙子僵尸进程无法回收
2.如果想回收孙子、重孙子僵尸进程,需在子、孙子进程添加回收僵尸进程代码
"""
import os
import time
import errno
import commands
import logging
logging.basicConfig(format="%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s|%(message)s", level=logging.DEBUG)
# record the zombie process dictionary
CHILD_ZOMBIE_DICT = {}
# allow zombie process time to live: unit seconds
ZOMBIE_TIME_TO_LIVE = 30
def get_child_zombie(parent_pid):
"""
get zombie child process that exceed the threshold
record the zombie process dictionary: {"zombie process pid": "timestamp"}
:param parent_pid: the current main process
:return: the zombie process pid
"""
# 1.get a list of all child process of the current main process
child_pid_cmd = "ps -ef | awk '{if($3==%s) print $2}' | sort | uniq"
child_pid_list = commands.getoutput(child_pid_cmd % (parent_pid))
child_pid_list = child_pid_list.split("n")
logging.info("main process [%s]: 1-child_pid_list: %s" % (parent_pid, child_pid_list))
# 2.get a list of all zombie processes on linux system
node_zombie_cmd = "ps -A -ostat,pid | grep -e '^[Zz]' | awk '{print $2}'"
node_zombie_list = commands.getoutput(node_zombie_cmd)
node_zombie_list = node_zombie_list.split("n")
logging.info("main process [%s]: 2-node_zombie_list: %s" % (parent_pid, node_zombie_list))
# 3.the intersection of the lists is the zombie child process under the current main process
child_zombie_list = list(set(child_pid_list).intersection(node_zombie_list))
logging.info("main process [%s]: 3-child_zombie_list: %s" % (parent_pid, child_zombie_list))
global CHILD_ZOMBIE_DICT
logging.info("main process [%s]: 4-CHILD_ZOMBIE_DICT: %s" % (parent_pid, CHILD_ZOMBIE_DICT))
if child_zombie_list:
for child_zombie_pid in child_zombie_list:
time_value = float(CHILD_ZOMBIE_DICT.get(child_zombie_pid, 0))
time_now = time.time()
if time_value:
# calculation time
time_diff = round(time_now - time_value, 2)
# recycling time exceeds the threshold child process
if time_diff > ZOMBIE_TIME_TO_LIVE:
CHILD_ZOMBIE_DICT.pop(child_zombie_pid)
return child_zombie_pid
else:
CHILD_ZOMBIE_DICT[child_zombie_pid] = time_now
return 0
else:
# if there is no zombie process under the current main process, clear the record
CHILD_ZOMBIE_DICT.clear()
return 0
def wait_child(ppid):
while True:
try:
# get the pid of the zombie child process that exceeds the threshold under the main process
child_zombie_pid = get_child_zombie(ppid)
if child_zombie_pid:
logging.info("main process [%s]: 5-child_zombie_pid: %s" % (ppid, child_zombie_pid))
child_pid, status = os.waitpid(int(child_zombie_pid), os.WNOHANG)
if child_pid == 0:
# no child process was immediately available
continue
return_code = status >> 8
logging.info(
"main process [%s]: child process: %s exit with return code: %s" % (ppid, child_pid, return_code))
else:
continue
except OSError as e:
if e.errno == errno.ECHILD:
logging.info("main process [%s]: no child process that need to wait" % (ppid))
else:
logging.error("main process [%s] OSError: %s" % (ppid, e))
except Exception as e:
logging.exception("main process [%s] Exception: %s" % (ppid, e))
finally:
time.sleep(5)
4.调用方法
# -*- coding: utf-8 -*-
"""
Author: tanggaomeng
date: 2021/11/21
Description:
"""
import os
import time
import threading
from recycling_zombie_processes import wait_child
def main():
for i in range(0, 1000):
time.sleep(5)
if __name__ == '__main__':
# recycling zombie processes
ppid = os.getpid()
thread = threading.Thread(target=wait_child, args=(ppid,))
thread.start()
main()



