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读写ini配置文件方法实例分析
Jun 30 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
Aug 30 Python
Python爬虫包BeautifulSoup实例(三)
Jun 17 Python
Python爬虫实现验证码登录代码实例
May 10 Python
Python中将两个或多个list合成一个list的方法小结
May 12 Python
python 实现创建文件夹和创建日志文件的方法
Jul 07 Python
基于django ManyToMany 使用的注意事项详解
Aug 09 Python
python切片(获取一个子列表(数组))详解
Aug 09 Python
pytorch载入预训练模型后,实现训练指定层
Jan 06 Python
Python3 中sorted() 函数的用法
Mar 24 Python
浅析Python 条件控制语句
Jul 15 Python
PyQt5爬取12306车票信息程序的实现
May 14 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
php zend解密软件绿色版测试可用
2008/04/14 PHP
php之static静态属性与静态方法实例分析
2015/07/30 PHP
php实现处理输入转义字符的代码
2015/11/08 PHP
php实现简单的权限管理的示例代码
2017/08/25 PHP
jquer之ajaxQueue简单实现代码
2011/09/15 Javascript
js汉字排序问题 支持中英文混排,兼容各浏览器,包括CHROME
2011/12/20 Javascript
js/jquery解析json和数组格式的方法详解
2014/01/09 Javascript
JS的encodeURI和java的URLDecoder.decode使用介绍
2014/05/08 Javascript
Windows8下搭建Node.js开发环境教程
2014/09/03 Javascript
jQuery使用之设置元素样式用法实例
2015/01/19 Javascript
jQuery插件slider实现拖动滑块选取价格范围
2015/04/30 Javascript
vue百度地图 + 定位的详解
2019/05/13 Javascript
js实现一个简易计算器
2020/03/30 Javascript
微信公众号生成新浪短网址的实现(快速生成)
2019/08/18 Javascript
js实现轮播图特效
2020/05/28 Javascript
js实现鼠标滑动到某个div禁止滚动
2020/09/17 Javascript
Python和C/C++交互的几种方法总结
2017/05/11 Python
Python 关于反射和类的特殊成员方法
2017/09/14 Python
python 实现判断ip连通性的方法总结
2018/04/22 Python
Python实现的拉格朗日插值法示例
2019/01/08 Python
纯python进行矩阵的相乘运算的方法示例
2019/07/17 Python
Nginx+Uwsgi+Django 项目部署到服务器的思路详解
2020/05/08 Python
python爬虫请求头的使用
2020/12/01 Python
HTML5 audio标签使用js进行播放控制实例
2015/04/24 HTML / CSS
canvas实现按住鼠标移动绘制出轨迹的示例代码
2018/02/05 HTML / CSS
莫斯科隐形眼镜网上商店:Linzi
2019/07/22 全球购物
SQL数据库笔试题
2016/03/08 面试题
英语专业毕业生求职简历的自我评价
2013/10/24 职场文书
加多宝凉茶广告词
2014/03/18 职场文书
教师产假请假条范文
2014/04/10 职场文书
销售团队激励口号
2014/06/06 职场文书
煤矿开采专业求职信
2014/07/08 职场文书
政风行风建设责任书
2014/07/23 职场文书
党员查摆剖析材料
2014/10/10 职场文书
沈阳故宫导游词
2015/01/31 职场文书
一劳永逸彻底解决pip install慢的办法
2021/05/24 Python