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 相关文章推荐
Python2.x中文乱码问题解决方法
Jun 02 Python
在Mac OS系统上安装Python的Pillow库的教程
Nov 20 Python
python从入门到精通(DAY 3)
Dec 20 Python
Python 加密的实例详解
Oct 09 Python
python 列表删除所有指定元素的方法
Apr 19 Python
解决Python requests库编码 socks5代理的问题
May 07 Python
在python 不同时区之间的差值与转换方法
Jan 14 Python
pyqt 实现为长内容添加滑轮 scrollArea
Jun 19 Python
基于python解线性矩阵方程(numpy中的matrix类)
Oct 21 Python
python socket 聊天室实例代码详解
Nov 14 Python
解决tensorflow/keras时出现数组维度不匹配问题
Jun 29 Python
python实现ROA算子边缘检测算法
Apr 05 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
如何跨站抓取别的站点的页面的补充
2006/10/09 PHP
关于php支持分块与断点续传文件下载功能代码
2014/05/09 PHP
关于扩展 Laravel 默认 Session 中间件导致的 Session 写入失效问题分析
2016/01/08 PHP
浅谈socket同步和异步、阻塞和非阻塞、I/O模型
2016/12/15 PHP
关于Laravel参数验证的一些疑与惑
2019/11/19 PHP
PHP与Web页面的交互示例详解二
2020/08/04 PHP
jQuery UI 应用不同Theme的办法
2010/09/12 Javascript
js的写法基础分析
2011/01/17 Javascript
innerHTML与jquery里的html()区别介绍
2012/10/12 Javascript
Javascript常用小技巧汇总
2015/06/24 Javascript
node模块机制与异步处理详解
2016/03/13 Javascript
百度多文件异步上传控件webuploader基本用法解析
2016/11/07 Javascript
详解有关easyUI的拖动操作中droppable,draggable用法例子
2017/06/03 Javascript
百度小程序自定义通用toast组件
2019/07/17 Javascript
解决vant中 tab栏遇到的坑 van-tabs
2020/11/04 Javascript
vue 使用localstorage实现面包屑的操作
2020/11/16 Javascript
Python 条件判断的缩写方法
2008/09/06 Python
一个基于flask的web应用诞生 bootstrap框架美化(3)
2017/04/11 Python
Python通过Django实现用户注册和邮箱验证功能代码
2017/12/11 Python
浅谈python可视化包Bokeh
2018/02/07 Python
Python scipy的二维图像卷积运算与图像模糊处理操作示例
2019/09/06 Python
如何使用python实现模拟鼠标点击
2020/01/06 Python
python绘制玫瑰的实现代码
2020/03/02 Python
详解pandas中iloc, loc和ix的区别和联系
2020/03/09 Python
python如何写出表白程序
2020/06/01 Python
Python内存泄漏和内存溢出的解决方案
2020/09/26 Python
总裁助理岗位职责
2014/02/17 职场文书
护理学院专科毕业生求职信
2014/06/28 职场文书
县委常委班子专题民主生活会查摆问题及整改措施
2014/09/27 职场文书
作风建设剖析材料
2014/10/06 职场文书
小学教育见习报告
2014/10/31 职场文书
2014年除四害工作总结
2014/12/06 职场文书
解决Golang time.Parse和time.Format的时区问题
2021/04/29 Golang
Python中22个万用公式的小结
2021/07/21 Python
Python实现Excel文件的合并(以新冠疫情数据为例)
2022/03/20 Python
mysql数据插入覆盖和时间戳的问题及解决
2022/03/25 MySQL