python处理僵尸进程

当你使用ps -ef 查看系统进程时,如果发现了带有defunct字样的进程信息时,恭喜你,你成功的制造了僵尸进程。僵尸进程产生的原因是,子进程已经退出了,但是父进程还在运行,可是父进程并没有对子进程进行回收,这就导致子进程一直存在,对资源是一种浪费,如果僵尸进程过多,系统性能会下降。在python项目里,如果不能正确的使用subprocess模块的Popen类,就很容易产生僵尸进程。

1. 什么情况下产生僵尸进程?

1.1 父进程先退出

父进程先退出,子进程还在运行,这时,子进程被init收养,当子进程结束后,init负责对子进程的资源进行回收,这种情况下,不会产生僵尸进程

1.2 子进程先退出,父进程进行wait()操作

父进程在启动子进程后,执行wait(),子进程退出后,这个wait()操作就负责回收子进程,这样也不会产生僵尸进程。但这样做有个致命的问题,wait是阻塞的,如果进行wait,主进程就什么都做不了。

1.3 子进程先退出,父进程不进行wait()操作

这种情况,百分百会产生僵尸进程,直到父进程退出,僵尸进程由init收养,init负责执行wait()处理。

2. 如何避免产生僵尸进程

2.1 fork两次

  1. 第一次fork,父进程wait 子进程
  2. 第二次fork, 子进程产生孙子进程后退出,由父进程回收
  3. 孙子进程刚产生,它的父亲就退出了,成了孤儿进程,由init收养

这样,通过两次fork,就避免了僵尸进程的产生

2.2 使用signal(SIGCHLD, SIG_IGN)

子进程在退出时,会向父进程发送SIGCHLD信号,SIG_IGN 是忽略的意思,在python代码里加入这样一段

import signal

signal.signal(signal.SIGCHLD, signal.SIG_IGN)

这段代码通知内核,自己对子进程的退出不关心,让内核来回收,这个方法看起来十分简单,但是并不是在所有系统上都有效,在mac和linux系统下,这种方法是可行的,其他的系统我没有试验,强调一点,在docker环境下,init进程其实是docker主程序的进程,因此在docker里,即便是linux系统这种方法也不行。

2.3 在signal handler中调用 waitpid

为SIGCHLD信号注册一个处理方法,当子进程退出后,使用waitpid来处理子进程的退出,如此处理,父进程不会发生阻塞

test.py

import errno
import time
import sys
import os
import signal
from subprocess import Popen

def wait_child(signum, frame):
    try:
        while True:
            childpid, status = os.waitpid(-1, os.WNOHANG)
            if childpid == 0:
                print('没有立即可用的子进程')
                break
            exitcode = status >> 8
            print('子进程 %s 退出,状态码是 exitcode %s' % childpid, exitcode)
    except OSError as e:
        if e.errno == errno.ECHILD:
            print("没有需要等待wait的子进程")
        else:
            raise

signal.signal(signal.SIGCHLD, wait_child)
Popen([sys.executable, './app.py'])

for i in range(10):
    time.sleep(5)

app.py

import time

for i in range(3):
    print(i)
    time.sleep(5)

执行test.py 脚本后,会通过子进程执行app.py, app.py会先结束,结束时会发送signal.SIGCHLD给父进程test.py, 在test.py中对信号进行处理。这里对os.waitpid(-1, os.WNOHANG) 进行解释

  1. -1 表示任意子进程
  2. os.WNOHANG 表示如果没有需要wait的子进程,则waitpid方法立即返回

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案