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开发环境PyScripter中文乱码问题解决方案
Sep 11 Python
Python中反射和描述器总结
Sep 23 Python
Python图像处理之gif动态图的解析与合成操作详解
Dec 30 Python
使用python将请求的requests headers参数格式化方法
Jan 02 Python
Python enumerate函数功能与用法示例
Mar 01 Python
pycharm创建scrapy项目教程及遇到的坑解析
Aug 15 Python
Python编程快速上手——正则表达式查找功能案例分析
Feb 28 Python
Python对象的属性访问过程详解
Mar 05 Python
python中pandas库中DataFrame对行和列的操作使用方法示例
Jun 14 Python
keras中的History对象用法
Jun 19 Python
python使用建议技巧分享(三)
Aug 18 Python
在pyCharm中下载第三方库的方法
Apr 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 数组使用详解 推荐
2011/06/02 PHP
php中关于codeigniter的xmlrpc的类在进行数据交换时的类型问题
2011/07/03 PHP
微信公众平台消息接口校验与消息接口响应实例
2014/12/23 PHP
基于yaf框架和uploadify插件,做的一个导入excel文件,查看并保存数据的功能
2017/01/24 PHP
jquery自动填充勾选框即把勾选框打上true
2014/03/24 Javascript
javascript正则匹配汉字、数字、字母、下划线
2014/04/10 Javascript
JavaScript中的console.dir()函数介绍
2014/12/29 Javascript
jQuery中:reset选择器用法实例
2015/01/04 Javascript
自己动手手写jQuery插件总结
2015/01/20 Javascript
javascript截取字符串小结
2015/04/28 Javascript
FullCalendar日历插件应用之数据展现(一)
2015/12/23 Javascript
jquery的ajax提交form表单的两种方法小结(推荐)
2016/05/25 Javascript
简单实现js页面切换功能
2021/01/10 Javascript
基于JavaScript实现添加到购物车效果附源码下载
2016/08/22 Javascript
基于BootStrap栅格栏系统完成网站底部版权信息区
2016/12/23 Javascript
解决ajax不能访问本地文件问题(利用js跨域原理)
2017/01/24 Javascript
详解vue-router和vue-cli以及组件之间的传值
2017/07/04 Javascript
快速掌握jquery分页插件jqPaginator的使用方法
2017/08/09 jQuery
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
2018/01/05 NodeJs
使用Python发送邮件附件以定时备份MySQL的教程
2015/04/25 Python
python模块之sys模块和序列化模块(实例讲解)
2017/09/13 Python
对django中render()与render_to_response()的区别详解
2018/10/16 Python
对python中的iter()函数与next()函数详解
2018/10/18 Python
python中退出多层循环的方法
2018/11/27 Python
CentOS7下安装python3.6.8的教程详解
2020/01/03 Python
夏威夷咖啡公司:Hawaii Coffee Company
2019/09/19 全球购物
String s = new String(“xyz”);创建了几个String Object?
2015/08/05 面试题
医科大学生的自我评价
2013/12/04 职场文书
银行简历自我评价
2014/02/11 职场文书
团队精神口号
2014/06/06 职场文书
机械设计及其自动化专业求职信
2014/06/09 职场文书
青年文明号口号
2014/06/17 职场文书
企业趣味活动方案
2014/08/21 职场文书
临时工聘用合同协议书
2014/10/29 职场文书
公路施工安全责任书
2015/05/08 职场文书
Win11无法访问设备和打印机 如何解决页面空白
2022/04/09 数码科技