【问题标题】:subprocess.call() not killing the process on timeoutsubprocess.call() 不会在超时时终止进程
【发布时间】:2020-10-21 14:14:35
【问题描述】:

我不打算详细说明为什么我需要这样做,但我的问题如下:我有一个包含一些我需要运行的 shell 命令的字符串,它可能有其中的一些输出重定向。考虑一个简单的例子:

cmd = "yes > output.txt"

我希望能够在超时的情况下通过 python 运行此命令,这样如果存在无限循环,该命令就不会永远运行。我正在使用子流程模块。这是我的代码:

import subprocess

subprocess.call("yes > output.txt", timeout=1, shell=True)

当我运行它时,我在 1 秒后按预期收到了 subprocess.TimeoutExpired 异常,但该进程没有被终止。如果我查看htop,我仍然看到yes 进程正在运行。

奇怪的是,如果我通过解释器运行它,并在提示 引发异常后按Ctrl+C,则会终止进程。我只是在这里做一些愚蠢的事情吗?

我在带有 Python 3.7.7 的 macOS Catalina 以及带有 Python 3.6.9 的 Ubuntu 18.04 上都得到了这种行为

编辑:

如果我的命令将输出重定向到文件,我不确定为什么会出现不一致。例如:

subprocess.call("sleep 100", timeout=1, shell=True)

确实在超时时终止进程。但是,以下内容:

subprocess.call("sleep 100 > f", timeout=1, shell=True)

不会杀死进程。我知道在这种情况下,实际上没有任何内容被重定向到该文件。


更重要的是,我的实际问题是,我将如何在所有情况下在超时后杀死子进程?

【问题讨论】:

标签: python subprocess


【解决方案1】:

timeout 参数不是允许子进程的时间,而只是允许父进程等待其子进程结束的时间。如果孩子没有在允许的时间内结束,那么孩子不会发生任何事情,并且父母会收到TimeoutExpired 异常。所以你观察到的是设计

当您在交互式终端会话中键入 Ctrl C 时,会将 SIGINT 中断发送到前台进程的进程组中的所有进程。由于您尚未明确启动新进程组,因此您的 Python 进程的子进程将共享相同的进程组并接收 SIGINT。再次,您观察到的是设计

sleep 程序很特别。它在内部取决于 SIGALARM 中断,subprocess.call 与 timeout 参数一样。同样,信号被发送到整个进程组,因此子进程终止。但是我没有解释为什么将输出重定向到文件会阻止孩子被杀死。

如果你想让孩子被强制终止,你不应该使用call,而是明确地创建和管理一个Process对象:

p = subprocess.Process("yes > output.txt", shell=True)
try:
    p.wait(timeout=1)
except subprocess.TimeoutExpired:
    p.terminate()

注意未经测试的代码,因此可能存在拼写错误...

【讨论】:

  • 谢谢!我已经更新了问题,如果你可以看看。
  • 更新后的解决方案似乎不起作用。在p.terminate 之后,“是”进程仍在运行。知道为什么吗? PS:我认为是subprocess.Popen,或者至少我用过。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2012-11-06
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多