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使用xlrd模块操作Excel数据导入的方法
May 26 Python
django开发之settings.py中变量的全局引用详解
Mar 29 Python
Python callable()函数用法实例分析
Mar 17 Python
pandas分别写入excel的不同sheet方法
Dec 11 Python
Python中asyncio模块的深入讲解
Jun 10 Python
Python实现性能自动化测试竟然如此简单
Jul 30 Python
基于python分析你的上网行为 看看你平时上网都在干嘛
Aug 13 Python
python实现登录密码重置简易操作代码
Aug 14 Python
对tensorflow中的strides参数使用详解
Jan 04 Python
python实现ip地址的包含关系判断
Feb 07 Python
Django Admin后台模型列表页面如何添加自定义操作按钮
Nov 11 Python
python3中确保枚举值代码分析
Dec 02 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抽奖小程序的实现代码
2013/06/18 PHP
ThinkPHP3.1新特性之多层MVC的支持
2014/06/19 PHP
详解PHP错误日志的获取方法
2015/07/20 PHP
PHP实现求解最长公共子串问题的方法
2017/11/17 PHP
腾讯与新浪的通过IP地址获取当前地理位置(省份)的接口
2010/07/26 Javascript
Javascript调用C#代码
2011/01/17 Javascript
JS+css 图片自动缩放自适应大小
2013/08/08 Javascript
在javascript中实现函数数组的方法
2013/12/25 Javascript
JavaScript数据结构与算法之栈详解
2015/03/12 Javascript
js判断radiobuttonlist的选中值显示/隐藏其它模块的实现方法
2016/08/25 Javascript
D3.js进阶系列之CSV表格文件的读取详解
2017/06/06 Javascript
Vue ElementUI之Form表单验证遇到的问题
2017/08/21 Javascript
Vue异步加载about组件
2017/10/31 Javascript
vue2.0路由切换后页面滚动位置不变BUG的解决方法
2018/03/14 Javascript
vue 弹框产生的滚动穿透问题的解决
2018/09/21 Javascript
js实现移动端tab切换时下划线滑动效果
2019/09/08 Javascript
LayUI switch 开关监听 获取属性值、更改状态的方法
2019/09/21 Javascript
vue通过v-html指令渲染的富文本无法修改样式的解决方案
2020/05/20 Javascript
[04:16]DOTA2英雄梦之声_第09期_斧王
2014/06/21 DOTA
Python构造函数及解构函数介绍
2015/02/26 Python
python中self原理实例分析
2015/04/30 Python
Python中http请求方法库汇总
2016/01/06 Python
python3.4+pycharm 环境安装及使用方法
2019/06/13 Python
Python符号计算之实现函数极限的方法
2019/07/15 Python
django页面跳转问题及注意事项
2019/07/18 Python
如何在django中运行scrapy框架
2020/04/22 Python
Python键鼠操作自动化库PyAutoGUI简介(小结)
2020/05/17 Python
python爬虫多次请求超时的几种重试方法(6种)
2020/12/01 Python
第二层交换机和路由器的区别?第三层交换机和路由器的区别?
2013/05/23 面试题
仓库规划计划书
2014/04/28 职场文书
社区春季防火方案
2014/06/02 职场文书
学校学雷锋活动总结
2014/06/26 职场文书
美术专业自荐信
2014/07/07 职场文书
上市公司董事长岗位职责
2015/04/16 职场文书
大学生创业,为什么都会选择快餐饮?
2019/08/08 职场文书
设置IIS Express并发数
2022/07/07 Servers