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 获取 Linux 系统信息的代码
Jul 13 Python
使用Python导出Excel图表以及导出为图片的方法
Nov 07 Python
使用Python下载歌词并嵌入歌曲文件中的实现代码
Nov 13 Python
浅谈Python中用datetime包进行对时间的一些操作
Jun 23 Python
使用python编写监听端
Apr 12 Python
Python logging模块用法示例
Aug 28 Python
python提取具有某种特定字符串的行数据方法
Dec 11 Python
对python借助百度云API对评论进行观点抽取的方法详解
Feb 21 Python
django的settings中设置中文支持的实现
Apr 28 Python
python获取磁盘号下盘符步骤详解
Jun 19 Python
python打造爬虫代理池过程解析
Aug 15 Python
解决python 输出到csv 出现多空行的情况
Mar 24 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
[FAQ]PHP中的一些常识:类篇
2006/10/09 PHP
利用PHP制作简单的内容采集器的代码
2007/11/28 PHP
解析PHP生成静态html文件的三种方法
2013/06/18 PHP
php中文验证码实现方法
2015/06/18 PHP
PHP实现求连续子数组最大和问题2种解决方法
2017/12/26 PHP
PHP排序算法之希尔排序(Shell Sort)实例分析
2018/04/20 PHP
JS 文字符串转换unicode编码函数
2009/05/30 Javascript
纯javascript实现自动发送邮件
2015/10/21 Javascript
对象题目的一个坑 理解Javascript对象
2015/12/22 Javascript
js学习总结之dom2级事件基础知识详解
2017/07/27 Javascript
前端图片懒加载(lazyload)的实现方法(提高用户体验)
2017/08/21 Javascript
解决option标签selected="selected"属性失效的问题
2017/11/06 Javascript
vue.js使用v-model指令实现的数据双向绑定功能示例
2018/05/22 Javascript
原生JS封装_new函数实现new关键字的功能
2018/08/12 Javascript
微信小程序之判断页面滚动方向的示例代码
2018/08/30 Javascript
Vue开发之watch监听数组、对象、变量操作分析
2019/04/25 Javascript
es6中reduce的基本使用方法
2019/09/10 Javascript
Python实现字典依据value排序
2016/02/24 Python
详解Python中的变量及其命名和打印
2016/03/11 Python
Python实现命令行通讯录实例教程
2016/08/18 Python
Python获取本机所有网卡ip,掩码和广播地址实例代码
2018/01/22 Python
Python使用wget实现下载网络文件功能示例
2018/05/31 Python
python爬虫框架scrapy实现模拟登录操作示例
2018/08/02 Python
python中time库的实例使用方法
2019/10/31 Python
纯CSS实现预加载动画效果
2017/09/06 HTML / CSS
详解如何在登录过期后跳出Ifram框架
2020/09/10 HTML / CSS
摄影实习自我鉴定
2013/09/20 职场文书
小学教师听课制度
2014/02/01 职场文书
2014年防汛工作总结
2014/12/08 职场文书
颐和园导游词
2015/01/30 职场文书
律师函格式范本
2015/05/27 职场文书
埃及王子观后感
2015/06/16 职场文书
红色经典观后感
2015/06/18 职场文书
2017公司年会主持人开幕词
2016/03/04 职场文书
Vue的列表之渲染,排序,过滤详解
2022/02/24 Vue.js
我的收音机情缘
2022/04/05 无线电