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文件读写操作与linux shell变量命令交互执行的方法
Jan 14 Python
Python中列表的一些基本操作知识汇总
May 20 Python
Python实现批量下载图片的方法
Jul 08 Python
python简单获取数组元素个数的方法
Jul 13 Python
举例讲解Python中metaclass元类的创建与使用
Jun 30 Python
CentOS7.3编译安装Python3.6.2的方法
Jan 22 Python
简单了解python单例模式的几种写法
Jul 01 Python
Django 项目重命名的实现步骤解析
Aug 14 Python
python3 实现调用串口功能
Dec 26 Python
基于Python获取照片的GPS位置信息
Jan 20 Python
Python 之 Json序列化嵌套类方式
Feb 27 Python
Python 高级库15 个让新手爱不释手(推荐)
May 15 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 利用AJAX获取网页并输出的实现代码(Zjmainstay)
2012/08/31 PHP
CURL状态码列表(详细)
2013/06/27 PHP
php mailer类调用远程SMTP服务器发送邮件实现方法
2016/03/04 PHP
微信支付开发维权通知实例
2016/07/12 PHP
PHP基于GD2函数库实现验证码功能示例
2019/01/27 PHP
PHP设计模式之单例模式定义与用法分析
2019/03/26 PHP
PHP+fiddler抓包采集微信文章阅读数点赞数的思路详解
2019/12/20 PHP
关于IFRAME 自适应高度的研究
2006/07/20 Javascript
javascript 写的一个简单的timer
2009/07/30 Javascript
表头固定(利用jquery实现原理介绍)
2012/11/08 Javascript
判断javascript的数据类型(示例代码)
2013/12/11 Javascript
浅谈Javascript Base64 加密解密
2014/12/28 Javascript
Javascript中For In语句用法实例
2015/05/14 Javascript
JavaScript实现列表分页功能特效
2015/05/15 Javascript
jQuery表单验证功能实例
2015/08/28 Javascript
jQuery表格插件datatables用法详解
2020/11/23 Javascript
jquery css实现邮箱自动补全
2016/11/14 Javascript
详解angular中如何监控dom渲染完毕
2017/01/03 Javascript
windows下vue-cli导入bootstrap样式
2017/04/25 Javascript
JavaScript实现图片拖曳效果
2017/09/08 Javascript
vue2.0实现的tab标签切换效果(内容可自定义)示例
2019/02/11 Javascript
Node.js如何对SQLite的async/await封装详解
2019/02/14 Javascript
Vue.js 实现地址管理页面思路详解(地址添加、编辑、删除和设置默认地址)
2019/12/11 Javascript
Vue双向绑定实现原理与方法详解
2020/05/07 Javascript
21行Python代码实现拼写检查器
2016/01/25 Python
python Matplotlib画图之调整字体大小的示例
2017/11/20 Python
详解python3 + Scrapy爬虫学习之创建项目
2019/04/12 Python
Python使用到第三方库PyMuPDF图片与pdf相互转换
2019/05/03 Python
Python实现哲学家就餐问题实例代码
2020/11/09 Python
美国背景检查、公共记录和人物搜索网站:BeenVerified
2018/02/25 全球购物
百度吧主申请感言
2014/01/12 职场文书
晚宴邀请函范文
2014/01/15 职场文书
群众路线教育党课主持词
2014/04/01 职场文书
纪委书记群众路线整改措施思想汇报
2014/10/09 职场文书
《卖火柴的小女孩》教学反思
2016/02/19 职场文书
Python实现简繁体转换
2021/06/07 Python