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脚本在Nginx和uwsgi上部署MoinMoin的教程
May 05 Python
对python pandas读取剪贴板内容的方法详解
Jan 24 Python
pandas 对group进行聚合的例子
Dec 27 Python
Python实现剪刀石头布小游戏(与电脑对战)
Dec 31 Python
关于python 的legend图例,参数使用说明
Apr 17 Python
Python 通过监听端口实现唯一脚本运行方式
May 05 Python
Python flask框架实现浏览器点击自定义跳转页面
Jun 04 Python
卸载tensorflow-cpu重装tensorflow-gpu操作
Jun 23 Python
python代码能做成软件吗
Jul 24 Python
pycharm专业版远程登录服务器的详细教程
Sep 15 Python
python上下文管理的使用场景实例讲解
Mar 03 Python
教你使用Python获取QQ音乐某个歌手的歌单
Apr 03 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
模板引擎Smarty深入浅出介绍
2006/12/06 PHP
yii2中使用Active Record模式的方法
2016/01/09 PHP
PHP实现链式操作的三种方法详解
2017/11/16 PHP
Laravel Eloquent分表方法并使用模型关联的实现
2019/11/25 PHP
JavaScript Cookie的读取和写入函数
2009/12/08 Javascript
return false,对阻止事件默认动作的一些测试代码
2010/11/17 Javascript
prettify 代码高亮着色器google出品
2010/12/28 Javascript
Javascript拓展String方法小结
2013/07/08 Javascript
javascript 10进制和62进制的相互转换
2014/07/31 Javascript
JavaScript截取字符串的2个函数介绍
2014/08/27 Javascript
jquery实现简单手风琴菜单效果实例
2015/06/13 Javascript
jQuery.trim() 函数及trim()用法详解
2015/10/26 Javascript
详解AngularJS如何实现跨域请求
2016/08/22 Javascript
jQuery+json实现动态创建复杂表格table的方法
2016/10/25 Javascript
bootstrap-datetimepicker实现只显示到日期的方法
2016/11/25 Javascript
关于JS Lodop打印插件打印Bootstrap样式错乱问题的解决方案
2016/12/23 Javascript
three.js快速入门【推荐】
2017/01/21 Javascript
JavaScript实现旋转轮播图
2020/08/18 Javascript
jQuery中$原理实例分析
2018/08/13 jQuery
百度小程序自定义通用toast组件
2019/07/17 Javascript
vue router返回到指定的路由的场景分析
2020/11/10 Javascript
[02:05]DOTA2完美大师赛趣味视频之看我表演
2017/11/18 DOTA
python创建只读属性对象的方法(ReadOnlyObject)
2013/02/10 Python
在django中使用自定义标签实现分页功能
2017/07/04 Python
python测试mysql写入性能完整实例
2018/01/18 Python
python求平均数、方差、中位数的例子
2019/08/22 Python
Keras 数据增强ImageDataGenerator多输入多输出实例
2020/07/03 Python
python3爬虫中多线程进行解锁操作实例
2020/11/25 Python
PyCharm Ctrl+Shift+F 失灵的简单有效解决操作
2021/01/15 Python
大四自我鉴定范文
2013/10/06 职场文书
客服专员岗位职责
2014/02/28 职场文书
土木工程求职信
2014/05/29 职场文书
球队口号
2014/06/18 职场文书
伏羲庙导游词
2015/02/09 职场文书
护士求职自荐信范文
2015/03/04 职场文书
银行保安拾金不昧表扬稿
2015/05/05 职场文书