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解析网页源代码中的115网盘链接实例
Sep 30 Python
python实现应用程序在右键菜单中添加打开方式功能
Jan 09 Python
python 多维切片之冒号和三个点的用法介绍
Apr 19 Python
Python函数any()和all()的用法及区别介绍
Sep 14 Python
python 美化输出信息的实例
Oct 15 Python
Python队列、进程间通信、线程案例
Oct 25 Python
Python有参函数使用代码实例
Jan 06 Python
tensorflow 查看梯度方式
Feb 04 Python
Python3将ipa包中的文件按大小排序
Apr 17 Python
python3中的logging记录日志实现过程及封装成类的操作
May 12 Python
Django用户登录与注册系统的实现示例
Jun 03 Python
OpenCV图片漫画效果的实现示例
Aug 18 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函数(简单整理)
2010/04/30 PHP
解析php dirname()与__FILE__常量的应用
2013/06/24 PHP
推荐10个提供免费PHP脚本下载的网站
2014/12/31 PHP
PHP date_default_timezone_set()设置时区操作实例分析
2020/05/16 PHP
javascript学习笔记(六) Date 日期类型
2012/06/19 Javascript
javascript和jquery修改a标签的href属性
2013/12/16 Javascript
jQuery获取对象简单实现方法小结
2014/10/30 Javascript
jQuery的animate函数实现图文切换动画效果
2015/05/03 Javascript
js实现超酷的照片墙展示效果图附源码下载
2015/10/08 Javascript
全屏滚动插件fullPage.js使用实例解析
2016/10/21 Javascript
jQuery图片切换动画效果
2017/02/28 Javascript
angularJS 发起$http.post和$http.get请求的实现方法
2017/05/18 Javascript
最常用的jQuery表单验证(简单)
2017/05/23 jQuery
解决Jquery下拉框数据动态获取的问题
2018/01/25 jQuery
vue2.0 实现导航守卫(路由守卫)
2018/05/21 Javascript
js使用swiper实现层叠轮播效果实例代码
2018/12/12 Javascript
原生js实现针对Dom节点的CRUD操作示例
2019/08/26 Javascript
九步学会Python装饰器
2015/05/09 Python
python实现简单的文字识别
2018/11/27 Python
python调用java的jar包方法
2018/12/15 Python
python解压TAR文件至指定文件夹的实例
2019/06/10 Python
python处理document文档保留原样式
2019/09/23 Python
pytorch自定义二值化网络层方式
2020/01/07 Python
Python如何实现在字符串里嵌入双引号或者单引号
2020/03/02 Python
Python3中对json格式数据的分析处理
2021/01/28 Python
详解Django中的FBV和CBV对比分析
2021/03/01 Python
JACK & JONES英国官方网站:欧洲领先的男装生产商
2017/09/27 全球购物
自我评价范文
2013/12/22 职场文书
《纸船和风筝》教学反思
2014/02/15 职场文书
房屋公证委托书
2014/04/03 职场文书
计算机毕业生求职信
2014/06/10 职场文书
北京故宫的导游词
2015/01/31 职场文书
党员转正介绍人意见
2015/06/03 职场文书
500字作文之难忘的同学
2019/12/20 职场文书
python神经网络编程之手写数字识别
2021/05/08 Python
SQLServer之常用函数总结详解
2021/08/30 SQL Server