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使用webbrowser浏览指定url的方法
Apr 04 Python
对于Python的框架中一些会话程序的管理
Apr 20 Python
python如何在终端里面显示一张图片
Aug 17 Python
简单谈谈Python中的几种常见的数据类型
Feb 10 Python
Python实用技巧之列表、字典、集合中根据条件筛选数据详解
Jul 11 Python
python 将对象设置为可迭代的两种实现方法
Jan 21 Python
python openCV获取人脸部分并存储功能
Aug 28 Python
Python命令行参数解析工具 docopt 安装和应用过程详解
Sep 26 Python
Python自动化测试笔试面试题精选
Mar 12 Python
python如何写出表白程序
Jun 01 Python
python能在浏览器能运行吗
Jun 17 Python
Pycharm如何自动生成头文件注释
Nov 14 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和Mysqlweb应用开发核心技术 第1部分 Php基础-1 开始了解php
2011/07/03 PHP
javascript 网页跳转的方法
2008/12/24 Javascript
按给定几率进行随机抽取的js代码
2010/12/28 Javascript
JS控制阿拉伯数字转为中文大写示例代码
2013/09/04 Javascript
javascript中call和apply方法浅谈
2013/09/27 Javascript
网站繁简切换的JS遇到页面卡死的解决方法
2014/03/12 Javascript
js使用递归解析xml
2014/12/12 Javascript
js实现的捐赠管理完整实例
2015/01/20 Javascript
举例讲解Node.js中的Writable对象
2015/07/29 Javascript
微信小程序前端源码逻辑和工作流
2016/09/25 Javascript
JS 对java返回的json格式的数据处理方法
2016/12/05 Javascript
Bootstarp 基础教程之表单部分实例代码
2017/02/03 Javascript
js判断文件类型大小并给出提示的实现方法
2018/01/03 Javascript
详解VueJS应用中管理用户权限
2018/02/02 Javascript
JavaScript查看代码运行效率console.time()与console.timeEnd()用法
2019/01/18 Javascript
小程序实现层叠卡片滑动效果
2019/08/26 Javascript
layui表格设计以及数据初始化详解
2019/10/26 Javascript
VUE前端从后台请求过来的数据进行转换数据结构操作
2020/11/11 Javascript
不要用强制方法杀掉python线程
2017/02/26 Python
python 生成器协程运算实例
2017/09/04 Python
django 通过ajax完成邮箱用户注册、激活账号的方法
2018/04/17 Python
基于python requests库中的代理实例讲解
2018/05/07 Python
python自动化报告的输出用例详解
2018/05/30 Python
使用pycharm在本地开发并实时同步到服务器
2019/08/02 Python
解决jupyter notebook 前面书写后面内容消失的问题
2020/04/13 Python
pandas.DataFrame.drop_duplicates 用法介绍
2020/07/06 Python
如何在Python3中使用telnetlib模块连接网络设备
2020/09/21 Python
selenium+超级鹰实现模拟登录12306
2021/01/24 Python
纽约21世纪百货官网:Century 21
2016/08/27 全球购物
Laura官网:加拿大女性的顶级时尚目的地
2019/09/20 全球购物
5.12护士节活动总结
2015/02/10 职场文书
运动会宣传稿100字
2015/07/23 职场文书
宿舍卫生管理制度
2015/08/05 职场文书
怎样写好演讲稿题目?
2019/08/21 职场文书
Redis字典实现、Hash键冲突及渐进式rehash详解
2021/09/04 Redis
Apache Hudi集成Spark SQL操作hide表
2022/03/31 Servers