如何在stdout/stderr无活动时终止子进程

1 投票
2 回答
1232 浏览
提问于 2025-04-17 19:24

我正在通过一个Python脚本运行一个程序(现在使用的是os.system)。不过,有时候这个程序会在某个地方卡住,如果在一定时间内没有输出到标准输出(stdout)或标准错误(stderr),我想把它杀掉。简单的超时设置不太管用,因为这个程序通常需要运行很长时间(可能是几个小时到几天),而且有时候它会在还没完成之前就卡住。

看起来subprocess.Popen是个不错的选择,但我还没找到一个好的例子来说明怎么做。我还想把标准输出和标准错误写入一个文件。

根据一些例子,我在考虑像这样做:

p = Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None)

while True:
    line = p.stdout.readline()
    outfile.write(line)
    # save current time or something, compare to time of
    # previous loop, if larger than timeout, kill process

但我不太确定怎么实现时间循环,也不知道怎么确保while循环不会在进程自己结束(而不是卡住)时一直运行下去。任何建议都会很有帮助。

2 个回答

2

为了完整起见,这里是我最终使用的代码,利用了建议的 signal.alarm

import time
import shlex
import subprocess

logfile = open(log, 'w', 1)
# cmd is command to run
args = shlex.split(cmd)   # tokenise args list
p = subprocess.Popen(args, shell=False, bufsize=0, stdin=None,
                     stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

def _handler(signum, frame):
    print('Timeout of %s min reached, stopping execution' % timeout)
    p.kill()
    time.sleep(30)  # to ensure no ghost process is left running
    raise RuntimeError('Timeout')

signal.signal(signal.SIGALRM, _handler)
try:
    while True:
        signal.alarm(int(timeout))
        inline = p.stdout.readline()
        if not inline:
            break
        logfile.write(inline)
        signal.alarm(0)
except RuntimeError:
    logfile.close()
    return 0

p.communicate()   # wait for process to finish, get return code
logfile.close()
return p.returncode
2

试着用 signal.alarm 来设置一个定时器,每当接收到一行数据后就启动这个定时器。然后通过处理 SIGALRM 信号,检查自从上一次接收到数据以来是否已经过了太长时间。

撰写回答