python僵尸进程产生的原因


Posted in Python onJuly 21, 2017

在 unix 或 unix-like 的系统中,当一个子进程退出后,它就会变成一个僵尸进程,如果父进程没有通过 wait 系统调用来读取这个子进程的退出状态的话,这个子进程就会一直维持僵尸进程状态。

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 命令来清除。

本文将探讨如何手动制造一个僵尸进程以及清除僵尸进程的办法。

手动制造一个僵尸进程

为了便于后面讲解清除僵尸进程的方法,我们使用日常开发中经常使用的 multiprocessing 模块来制造僵尸进程(准确的来说是制造一个长时间维持僵尸进程状态的子进程):

$ 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 变成了僵尸进程。

既然已经可以控制僵尸进程的产生了,那我们就可以进入下一步如何清除僵尸进程了。

清除僵尸进程有两种方法:

•第一种方法就是结束父进程。当父进程退出的时候僵尸进程随后也会被清除。
• 第二种方法就是通过 wait 调用来读取子进程退出状态。我们可以通过处理 SIGCHLD 信号,在处理程序中调用 wait 系统调用来清除僵尸进程。

处理 SIGCHLD 信号

子进程退出时系统会向父进程发送 SIGCHLD 信号,父进程可以通过注册 SIGCHLD 信号处理程序,在信号处理程序中调用 wait
系统调用来清理僵尸进程。 $ 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 退出状态的子进程,立即返回不阻塞
      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

可以看到,子进程退出变成僵尸进程后,系统给父进程发送了 SIGCHLD 信号,我们在 SIGCHLD 信号的处理程序中通过 os.waitpid 调用 wait 系统调用后阻止了子进程一直处于僵尸进程状态,从而实现了清除僵尸进程的效果。

Python 相关文章推荐
简明 Python 基础学习教程
Feb 08 Python
在Python中使用Mako模版库的简单教程
Apr 08 Python
Python cookbook(数据结构与算法)让字典保持有序的方法
Feb 18 Python
Python利用openpyxl库遍历Sheet的实例
May 03 Python
使用PyInstaller将python转成可执行文件exe笔记
May 26 Python
Python3的介绍、安装和命令行的认识(推荐)
Oct 20 Python
python批量获取html内body内容的实例
Jan 02 Python
python实现while循环打印星星的四种形状
Nov 23 Python
python 在threading中如何处理主进程和子线程的关系
Apr 25 Python
Python devel安装失败问题解决方案
Jun 09 Python
安装Anaconda3及使用Jupyter的方法
Oct 27 Python
OpenCV+python实现膨胀和腐蚀的示例
Dec 21 Python
python下载图片实现方法(超简单)
Jul 21 #Python
Python基于Pymssql模块实现连接SQL Server数据库的方法详解
Jul 20 #Python
Python使用内置json模块解析json格式数据的方法
Jul 20 #Python
Python轻量级ORM框架Peewee访问sqlite数据库的方法详解
Jul 20 #Python
Python函数式编程
Jul 20 #Python
python 换位密码算法的实例详解
Jul 19 #Python
python实现rsa加密实例详解
Jul 19 #Python
You might like
WAR3重制版DOTA 5V5初体验
2020/04/09 DOTA
PHP中array_map与array_column之间的关系分析
2014/08/19 PHP
php中二维数组排序问题方法详解
2015/08/28 PHP
PHP实现字符串翻转功能的方法【递归与循环算法】
2017/11/03 PHP
Thinkphp5行为使用方法汇总
2017/12/21 PHP
JQuery获取当前屏幕的高度宽度的实现代码
2011/07/12 Javascript
jQuery使用一个按钮控制图片的伸缩实现思路
2013/04/19 Javascript
JS事件在IE与FF中的区别详细解析
2013/11/20 Javascript
jquery html动态生成select标签出问题的解决方法
2013/11/20 Javascript
jQuery 写的简单打字游戏可以提示正确和错误的次数
2014/07/01 Javascript
JavaScript 学习笔记之语句
2015/01/14 Javascript
js如何改变文章的字体大小
2016/01/08 Javascript
JavaScript驾驭网页-CSS与DOM
2016/03/24 Javascript
Jquery中map函数的用法
2016/06/03 Javascript
实用jquery操作表单元素的简单代码
2016/07/04 Javascript
获取IE浏览器Cookie信息的方法
2017/01/23 Javascript
jQuery基于ajax方式实现用户名存在性检查功能示例
2017/02/10 Javascript
js实现带三角符的手风琴效果
2017/03/01 Javascript
JavaScript中的普通函数和箭头函数的区别和用法详解
2017/03/21 Javascript
jQuery实现 RadioButton做必选校验功能
2017/06/15 jQuery
值得收藏的vuejs安装教程
2017/11/21 Javascript
JavaScript实现元素滚动条到达一定位置循环追加内容
2017/12/28 Javascript
JS实现判断图片是否加载完成的方法分析
2018/07/31 Javascript
基于bootstrap页面渲染的问题解决方法
2018/08/09 Javascript
javascript实现手动点赞效果
2019/04/09 Javascript
webpack + vue 打包生成公共配置文件(域名) 方便动态修改
2019/08/29 Javascript
vue项目中使用bpmn-自定义platter的示例代码
2020/05/11 Javascript
JavaScript中reduce()的5个基本用法示例
2020/07/19 Javascript
jQuery+ajax实现文件上传功能
2020/12/22 jQuery
SQLite3中文编码 Python的实现
2017/01/11 Python
Python中进程和线程的区别详解
2017/10/29 Python
Python 写入训练日志文件并控制台输出解析
2019/08/13 Python
CSS3教程(9):设置RGB颜色
2009/04/02 HTML / CSS
HTML5 Web 存储详解
2016/09/16 HTML / CSS
您的时尚,您的生活方式:DTLR Villa
2019/12/25 全球购物
辩论会主持词
2015/07/03 职场文书