signal
提供了跨平台的信号处理能力,但信号机制在不同系统上却有很大的差异。Python 没有处理一些本可以处理的不同,于是给使用者留下了一点隐藏的问题。
信号的跨平台差异
SIGINT
/SIGTERM
这是两个常常出现的信号,它们的处理函数往往被加入进程退出的程序。在 Unix 和 Windows 上都可以给它们注册对应的信号处理函数,发送却完全不同。
在 Windows 上给一个进程发送 SIGINT
信号,你应该使用的是 signal.CTRL_C_EVENT
,而不是像在 Unix 上一样使用 signal.SIGINT
。虽然你可以在 Windows 上调用 os.kill(pid, signal.SIGINT)
,但它没有你想要的作用。
而 SIGTREM
虽然在 Windows 上被定义了,但只允许进程使用 raise
(注意不是 Python 的 raise
语法) 给自己抛出。这意味着你无法让一个进程给另一个进程发送 SIGTERM
信号来触发对应的处理函数。在 Windows 上,Python 进程想给本进程发送一个 SIGTERM
信号可以使用 signal.raise_signal(signal.SIGTERM)
。
SIGBREAK
这是一个 Windows 上独有的信号,由 CTRL
+BRAEK
键触发,它类似于 CTRL
+C
触发的 SIGINT
,区别在于 SIGINT
的默认处理程序是抛出一个 KeyboardInterrupt
异常。
你可以通过 os.kill(pid, signal.CTRL_BREAK_EVENT)
的方式向共享同一个控制台的进程发送 SIGBREAK
信号。
当你在 Windows 控制台使用 CTRL
+BRAEK
或 CTRL
+C
触发对应的信号时要注意,这些信号会传递到与控制台连接的所有控制台进程。
其他信号
除了上述三个信号外,SIGILL
/SIGSEGV
/SIGABRT
也是 Windows 上支持的信号,但一般来说它们并不需要被用户程序处理。
Windows 平台只有这六种信号,其他信号无需考虑 Windows 平台的兼容性,使用起来也较为简单,不多赘述。
注册信号处理函数
在 C 语言里编写信号处理逻辑,当信号被处理过一次之后,需要重新注册信号处理函数。而在 Python 里不需要,因为 Python 替你重新注册了。
对同一个信号多次注册处理函数时,Python 只会留下最后一次注册的处理函数。这里就存在一个小技巧,比如处理 SIGINT
信号的时候,用户不停按 Ctrl+C 应该是非常急切的想要关闭。那么第一次触发信号之后,我们可以注册一个新的信号处理函数用于处理快速退出。
import signal
def f0(signum, frame):
print("Interrupted")
signal.signal(signal.SIGINT, lambda signum, frame: print("SIGINT"))
signal.signal(signal.SIGINT, f0)
while True:
import time
time.sleep(1)