Python 中如何处理信号和键盘中断?

How are signals and KeyboardInterrupt handled in Python?

我有两个 Python 脚本 foo.pybar.pyfoo.py 将通过 os.system() 调用 bar.py

#foo.py
import os

print os.getpid()
os.system("python dir/bar.py")
#bar.py
import time

time.sleep(10)
print "over"

假设foo.py的pid是123,如果程序正常终止,会打印

123
over

如果我在 运行 时键入 kill 123,我将得到以下输出

123
Terminated
over

如果我在 运行 时按 Ctrl-C,我会得到类似

的结果
123
^CTraceback (most recent call last):
  File "dir/bar.py", line 4, in <module>
    time.sleep(10)
KeyboardInterrupt

但是如果我在 运行 时键入 kill -SIGINT 123,程序似乎会忽略该信号并正常退出。

123
over

在我看来,
如果我输入 kill 123,子流程将不会受到影响。
如果我键入 Ctrl-C,两个进程都将终止。
如果我输入 kill -SIGINT 123 而子进程是 运行,信号将被忽略。

有人可以向我解释一下它是如何工作的吗?
Ctrl-Ckill -SIGINT 不应该是等价的吗?
如果我输入kill 123是不是保证子进程不受影响(如果恰好是运行)?

顺便说一句,我在Ubuntu 14.04。谢谢!

让我们依次考虑每种情况:

if I type kill 123, the sub-process will not be affected.

是的,这就是 kill [pid] 的工作原理。它只向您要终止的进程发送信号。如果要将信号发送给一组进程,则必须使用代表 process group.

的负数

if I type Ctrl-C, both processes will be terminated.

我假设你的意思是 "terminated by Ctrl-C"。实际上,情况并非如此:只有 child 被终止。如果您在 foo.py 的末尾添加这样一行 print "I'm a little teapot",您将看到该行被打印出来。发生的事情是 child 收到信号。 parent 然后从 os.system 继续。如果没有附加行,它 看起来 就像 parent 也受到 Ctrl-C 的影响,但事实并非如此,如附加行所示。

您 shell 确实将信号发送到与 tty 关联的进程组,其中 包括 parent。 但是os.system 使用system 调用,它会在进行调用的进程中阻止SIGINTSIGQUIT 信号。所以parent是免疫的。

如果您不使用os.system那么您的进程将受到SIGINT的影响。为 foo.py:

尝试此代码
import os
import subprocess

print os.getpid()
p = subprocess.Popen(["python", "dir/bar.py"])
p.wait()
print "I'm a little teapot"

如果您在运行时点击 Ctrl-C,您将得到两个回溯:一个来自 parent,一个来自 child:

$ python foo.py 
29626
^CTraceback (most recent call last):
  File "dir/bar.py", line 4, in <module>
Traceback (most recent call last):
  File "foo.py", line 8, in <module>
    time.sleep(10)
KeyboardInterrupt    p.wait()

  File "/usr/lib/python2.7/subprocess.py", line 1389, in wait
    pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
  File "/usr/lib/python2.7/subprocess.py", line 476, in _eintr_retry_call
    return func(*args)
KeyboardInterrupt

if I type kill -SIGINT 123 while the sub-process is running, the signal will be ignored.

见上文。

Isn't Ctrl-C and kill -SIGINT supposed to be equivalent?

Ctrl-C 确实发送 SIGINT 到与您在其中发出 Ctrl-C.

的 tty 关联的前台进程组

If I type kill 123 is it guaranteed that the sub-process will not be affected (if it happens to be running)?

它自己 kill 123 只会将信号发送给 pid 123 的进程。Children 不会受到影响。