python的子进程问题,popen(创建一个僵尸并陷入困境)

时间:2015-11-30 20:16:07

标签: python subprocess fork popen zombie-process

我对Python的问题(3.4)subprocess.popen

很少(曾经有几千个),对popen的调用似乎创建了另一个分叉进程,除了故意进程和挂起(可能等待?),导致故意进程变成僵尸。

这是呼叫顺序:

with subprocess.Popen(['prog', 'arg1', 'arg2'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
    std_out, std_err = p.communicate()
    p.wait()

注意:上面的调用序列是从分叉进程本身运行的(进程池的一种形式,请参阅下面的进程列表)

问题发生在多个程序中(例如7z)所以我认为问题出在调用者而不是被调用者。

prog是zombiefied,因此我假设p.wait()语句从未到达或未正确执行。

生成的进程列表(ps -ef输出):

my_user  18219 18212  9 16:16 pts/1    00:18:11 python3 script.py        # original process
my_user  1045  18219  0 16:18 pts/1    00:00:14 python3 script.py        # Intentionally forked from original (poor man's process pool) - Seems to be stuck or waiting
my_user  2834  1045   0 16:18 pts/1    00:00:00 [prog] <defunct>         # Program run by subprocess.popen - Zombie
my_user  2841  1045   0 16:18 pts/1    00:00:00 python3 script.py        # !!!! Should not be here, also stuck or waiting, never finishes

已编辑(根据要求添加了代码示例): 问题中的代码:

import os
import subprocess

pid = os.fork()
if pid == 0:
    # child
    file_name='test.zip'
    out_dir='/tmp'

    while True:
        with subprocess.Popen(['7z', 'x', '-y', '-p', '-o' + out_dir, file_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
            try:
                std_out, std_err = p.communicate(timeout=600)
            except subprocess.TimeoutExpired:
                p.kill()
                std_out, std_err = p.communicate()
                logging.critical('7z failed, a timeout has occurred during waiting')
            except:
                p.kill()
                p.wait()
                raise
            return_code = p.poll()

        # do something
else:
    # parent
    wpid, status = os.waitpid(pid, 0)
    exit_code = status >> 8

2 个答案:

答案 0 :(得分:1)

subprocess确实在运行命令之前分叉。这在PEP 324中提到(ctrl-f代表“fork”)。

原因是命令是使用exec运行的,替换执行的调用进程。

正如您所看到的,它与执行的脚本共享相同的pid,因此它实际上是相同的进程,但它不是正在运行的python解释器。

因此,只要子进程没有返回,调用者python进程就不能。

答案 1 :(得分:0)

我认为这是混合分叉和线程的效果,这在Linux中是一件坏事。以下是几个参考文献:

导入logging模块后,我相信您的流程是多线程的。 (在我的情况下,我有时看到我的程序在等待logging futex时挂起,有时在subprocess内等待,而子进程已成为僵尸时挂起。)该模块使用OS锁来确保它可以以线程安全的方式调用。一旦fork,该锁的状态将由子进程继承。因此,子进程(单线程但继承了父进程的内存)无法获取logging锁,因为在发生fork时锁有时会被锁定。

(我对我的解释并不十分自信。当我从使用multiprocessing的默认fork行为切换到使用spawn行为时,我的问题就消失了。在后者中,一个孩子没有继承其父亲的记忆,subprocesslogging不再对我造成影响。)