我使用python来测试某些东西。这可能需要很长时间,我想设置(全局)超时。我使用以下脚本(总结):
class TimeoutException(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutException()
# Halt problem after half an hour
signal.alarm(1800)
try:
while solution is None:
guess = guess()
try:
with open(solutionfname, 'wb') as solutionf:
solverprocess = subprocess.Popen(["solver", problemfname], stdout=solutionf)
solverprocess.wait()
finally:
# `solverprocess.poll() == None` instead of try didn't work either
try:
solverprocess.kill()
except:
# Solver process was already dead
pass
except TimeoutException:
pass
# Cancel alarm if it's still active
signal.alarm(0)
然而,它有时会产生孤儿进程,但我无法可靠地重现这种情况。有谁知道防止这种情况的正确方法是什么?
答案 0 :(得分:2)
杀死进程后,您只需wait
。
答案 1 :(得分:2)
kill()
方法的文档声明:
杀死孩子。在Posix操作系统上,该功能将
SIGKILL
发送给孩子。 在Windows上kill()
是terminate()
的别名。
换句话说,如果您不在Windows上,则仅向子进程发送信号。 这将创建一个僵尸进程,因为父进程没有读取子进程的返回值。
kill()
和terminate()
方法只是send_signal(SIGKILL)
和send_signal(SIGTERM)
的快捷方式。
尝试在wait()
之后添加对kill()
的调用。这甚至显示在communicate()
的文档中的示例中:
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
请注意communicate()
之后对kill()
的来电。 (它相当于调用wait()
并且还擦除了子进程的输出。)
我想澄清一件事:似乎你并不完全明白僵尸进程是什么。僵尸进程是一个终止进程。内核将进程保留在进程表中,直到父进程读取其退出状态。我相信子进程使用的所有内存实际上都是重用的;内核只需要跟踪这样一个进程的退出状态。
所以,你看到的僵尸进程没有运行。它们已经完全死了,这就是为什么它们被称为 zombie 。它们在进程表中“活着”,但根本没有运行。
调用wait()
完成此操作:等到子进程结束并读取退出状态。这允许内核从进程表中删除子进程。
答案 2 :(得分:1)
在Linux上,您可以使用python-prctl。
定义preexec函数,例如:
def pre_exec():
import signal
prctl.set_pdeathsig(signal.SIGTERM)
让你的Popen电话通过它。
subprocess.Popen(..., preexec_fn=pre_exec)
那很简单。如果父母去世,孩子的过程将会死亡,而不是孤儿。
如果你不喜欢python-prctl的外部依赖,你也可以使用较旧的prctl。而不是
prctl.set_pdeathsig(signal.SIGTERM)
你会有
prctl.prctl(prctl.PDEATHSIG, signal.SIGTERM)