【问题标题】:Kill children of Python subprocess after subprocess.TimeoutExpired is thrown在 subprocess.TimeoutExpired 被抛出后杀死 Python 子进程的子进程
【发布时间】:2017-07-04 15:23:19
【问题描述】:

我在 Python 中调用一个 shell 脚本,它产生多个子进程。如果两分钟后没有完成,我想终止该进程及其所有子进程。

有什么办法可以用 subprocess.run 做到这一点,还是必须重新使用 Popen?由于运行被阻塞,我无法将 pid 保存在某个地方以在额外的命令中杀死孩子。一个简短的代码示例:

try:
    subprocess.run(["my_shell_script"], stderr=subprocess.STDOUT, timeout=120)
except subprocess.TimeoutExpired:                                                                      
    print("Timeout during execution")

【问题讨论】:

  • 是的,popen 是首选。请注意,杀死孩子的孩子是不可能的(取决于my_shell_script)。它们可以分离。
  • 就我而言,我绝对希望它们被杀死,因为它们会产生非常高的负载。我可能会恢复使用 Popen 并为我的进程获取一个 pid 以终止进程组。

标签: python python-3.x subprocess


【解决方案1】:

此问题已作为bug 报告给 Python 开发人员。当 stderr 或 stdout 被重定向时,它似乎特别发生。 这是@Tanu 代码的更正确版本。

import subprocess as sp

try:
    proc = sp.Popen(['ls', '-l'], stdout=sp.PIPE, stderr=sp.PIPE)
    outs, errs = proc.communicate(timeout=120)
except sp.TimeoutExpired:
    proc.terminate()

Popen 不接受 timeout 作为参数。它必须传递给communicate。 在 Posix 操作系统上,terminatekill 更温和,因为它降低了创建僵尸进程的风险。

【讨论】:

    【解决方案2】:

    引用自文档:

    subprocess.run - 默认情况下不捕获 stdout 或 stderr。为此,请将 PIPE 传递给 stdout 和/或 stderr 参数。

    如果您不想,不必使用 Popen()。模块中的其他函数,如.call()、.Popen()。

    共有三个“文件”流:stdin 用于输入,stdout 和 stderr 用于输出。应用程序决定在哪里写什么;通常将错误和诊断信息发送到 stderr,其余发送到 stdout。要捕获这些输出中的任何一个的输出,请指定 subprocess.PIPE 参数,以便将“流”重定向到您的程序中。

    超时后杀死子进程:

    import os
    import signal
    import subprocess
    
    try:
        proc = subprocess.Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE, timeout=120)
    except subprocess.TimeoutExpired:
        os.kill(proc.pid, signal.SIGTERM)
    

    【讨论】:

    • 但这如何帮助我杀死我的子进程的子进程?正如您在示例中看到的那样,我已经捕获了 stderr。
    • 已编辑答案。
    • 但这不会调用进程组。在那种情况下,proc.terminate() 更便携(Windows)
    • 是的proc.terminate()proc.kill() 也是一个选项
    猜你喜欢
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 2020-06-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    相关资源
    最近更新 更多