python subprocess.call不';不能正确处理信号
(我正在使用Python 3.4.2) 我有一个script test.py,它处理SIGTERM等。但是,当它被其他脚本调用时,sig处理是不正确的 这是test.py:python subprocess.call不';不能正确处理信号,python,python-3.x,subprocess,signals,Python,Python 3.x,Subprocess,Signals,(我正在使用Python 3.4.2) 我有一个script test.py,它处理SIGTERM等。但是,当它被其他脚本调用时,sig处理是不正确的 这是test.py: #! /path/to/python3 import time import signal import sys def handleSIG(signal, frame): for i in range(10): print(i) sys.exit() for sig in [signal
#! /path/to/python3
import time
import signal
import sys
def handleSIG(signal, frame):
for i in range(10):
print(i)
sys.exit()
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT, signal.SIGHUP]:
signal.signal(sig, handleSIG)
time.sleep(30)
import subprocess
cmd = '/path/to/test.py'
subprocess.call(cmd)
如果我只调用“test.py”并执行“Ctrl+C”,那么它会将0,1,…,9打印到控制台。但是,如果我在另一个脚本中使用subprocess.call调用test.py,则只会打印0。例如,下面是另一个调用test.py的脚本:
#! /path/to/python3
import time
import signal
import sys
def handleSIG(signal, frame):
for i in range(10):
print(i)
sys.exit()
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT, signal.SIGHUP]:
signal.signal(sig, handleSIG)
time.sleep(30)
import subprocess
cmd = '/path/to/test.py'
subprocess.call(cmd)
奇怪的是,使用subprocess.Popen()可以消除这个错误。如果它的
等待被中断,python 3.3subprocess.call
实现会向它的子进程发送一个SIGKILL,这是由Ctrl-C(SIGINT->KeyboardInterrupt异常)引起的
因此,您会看到处理终端的SIGINT(发送到整个进程组)的子进程与父进程的SIGKILL之间的竞争
来自python 3.3源代码,为简洁起见进行了编辑:
def call(*popenargs, timeout=None, **kwargs):
with Popen(*popenargs, **kwargs) as p:
try:
return p.wait(timeout=timeout)
except:
p.kill()
p.wait()
raise
与python 2实现相比:
def call(*popenargs, **kwargs):
return Popen(*popenargs, **kwargs).wait()
多么令人不快的惊喜啊。在3.3中,当扩展wait
和call
接口以适应超时时,似乎引入了这种行为。我不认为这是正确的,我已经更新了:这个Python-3回归将通过在Python 3.7中修复。有关其他背景和讨论,请参阅和(已拒绝)
我最近也遇到了这个问题。@pilcrow给出的解释是正确的
OP仅仅使用Python 2实现(Popen(*popenargs,**kwargs).wait()
)的解决方案(在评论中)对我来说是不够的,因为我不能100%确定孩子在所有情况下都会响应SIGINT
。我还是希望它在暂停后被杀死
我决定简单地重新等待孩子(超时)
从技术上讲,这意味着我有可能延长孩子的寿命,使其超出原来的超时时间
,但这比不正确的清理行为要好。Popen也不会等待流程完成,因此不确定这在什么情况下会起作用all@PadraicCunningham我从命令行运行test2.py,并使用Ctrl-C终止它。对于Popen,我使用“subprocess.Popen(cmd).wait()”,因此它会等待cmd完成。顺便说一句,从subprocess.call()的实现中,可以看出子进程的信号处理功能将被阻止。如果没有打印任何内容,我会理解,但奇怪的是,它打印了一些东西(在我的例子中,0),但不是全部。您使用的是什么操作系统?linux(在windows下通过PuTTy登录)您可以提到一个解决方法:在父级中为signal.SIGINT
定义自己的处理程序(以避免键盘中断
异常)或者显式调用Popen().wait()
(如果不需要超时)。是。我认为OP已经意识到直接使用Popen
。对于自定义SIGINT处理程序,您希望它做什么?大概OP希望使用Ctrl-C向整个流程组发送一个SIGINT,重要的是,中断等待
@pilcrow非常感谢您的解释。我用subprocess.Popen(cmd).wait()替换了subprocess.call(cmd)
,解决了这个问题。是的,我的目标是使用Ctrl-C终止父进程并使所有子进程正常退出。@pilcrow:nothing(signal(SIGINT,lambda*a:None)
),如果子进程在SIGINT上退出(SIGINT已经发送到整个进程组),则不需要中断。wait()
。但是如果您可以消除所有.call()
调用(包括在第三方代码中),那么Popen().wait()
可能更可取。也许os.\u exit()
在信号处理程序中会更好。这将产生预期的行为——我们可能也希望父对象终止。无论哪种方式,都是一种非常特殊的解决不愉快情况的方法。