python僵尸进程产生的原因

发布时间 - 2026-01-11 02:26:25    点击率:

在 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  # 僵尸进程  # 多进程  # 僵尸  # 判断僵尸进程  # 就会  # 可以看到  # 种方法  # 信号处理  # 也会  # 是这样  # 长时间  # 我们可以  # 可以通过  # 如果没有  # 有两种  # 送了  # 会向  # 就可以  # 变成了  # 实现了  # 产生了  # generally  # error  # long 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Swift中switch语句区间和元组模式匹配  佛山网站制作系统,佛山企业变更地址网上办理步骤?  Android仿QQ列表左滑删除操作  EditPlus中的正则表达式 实战(1)  Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试  如何在 Pandas 中基于一列条件计算另一列的分组均值  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  湖南网站制作公司,湖南上善若水科技有限公司做什么的?  在centOS 7安装mysql 5.7的详细教程  Laravel如何实现多对多模型关联?(Eloquent教程)  利用vue写todolist单页应用  深圳网站制作的公司有哪些,dido官方网站?  如何快速完成中国万网建站详细流程?  Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理  如何在IIS服务器上快速部署高效网站?  如何获取免费开源的自助建站系统源码?  Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制  Laravel怎么集成Vue.js_Laravel Mix配置Vue开发环境  高端建站如何打造兼具美学与转化的品牌官网?  ,怎么在广州志愿者网站注册?  Laravel如何使用查询构建器?(Query Builder高级用法)  Laravel如何实现用户密码重置功能?(完整流程代码)  如何在VPS电脑上快速搭建网站?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】  微信小程序 HTTPS报错整理常见问题及解决方案  如何在景安服务器上快速搭建个人网站?  PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)  如何快速生成凡客建站的专业级图册?  Android自定义控件实现温度旋转按钮效果  Win11怎么设置默认图片查看器_Windows11照片应用关联设置  Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  如何在阿里云域名上完成建站全流程?  微信小程序 闭包写法详细介绍  如何注册花生壳免费域名并搭建个人网站?  浅析上传头像示例及其注意事项  高端企业智能建站程序:SEO优化与响应式模板定制开发  桂林网站制作公司有哪些,桂林马拉松怎么报名?  百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏  香港服务器WordPress建站指南:SEO优化与高效部署策略  Laravel如何使用Sanctum进行API认证?(SPA实战)  Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程  JS中对数组元素进行增删改移的方法总结  Laravel如何实现全文搜索功能?(Scout和Algolia示例)  Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】