Python - 信号处理



Python 中的信号处理允许您定义自定义处理程序,用于管理异步事件,例如来自键盘的中断或终止请求、警报甚至系统信号。您可以通过定义自定义处理程序来控制程序对各种信号的响应方式。Python 中的 signal 模块提供了设置 and management signal handlers 的机制。

信号处理程序是在接收到特定信号时执行的函数。signal.signal() 函数允许为信号定义自定义处理程序。信号模块提供了一种定义自定义处理程序的方法,这些处理程序将在接收到特定信号时执行。Python 中已经安装了一些默认处理程序,它们是 -

  • SIGPIPE 被忽略。
  • SIGINT 转换为 KeyboardInterrupt 异常。

常用信号

Python 信号处理程序在主解释器的主 Python 线程中执行,即使信号是在另一个线程中接收的。信号不能用于线程间通信。

以下是一些常见信号及其默认操作的列表 -

  • SIGINT − 来自键盘的中断 (Ctrl+C),这会引发 KeyboardInterrupt。
  • SIGTERM − 终止信号。
  • SIGALRM− 来自 alarm() 的计时器信号。
  • SIGCHLD - 子进程已停止或终止。
  • SIGUSR1 和 SIGUSR2 − 用户定义的信号。

设置 Signal Handler

要设置信号处理程序,我们可以使用 signal.signal() 函数。它允许您为 signals 定义自定义处理程序。处理程序将保持安装状态,直到显式重置,SIGCHLD 除外。

下面是一个使用 signal.signal() 函数和 SIGINT 处理程序设置信号处理程序的示例。


import signal
import time

def handle_signal(signum, frame):
	 	print(f"Signal {signum} received")

# Setting the handler for SIGINT
signal.signal(signal.SIGINT, handle_signal)

print("Press Ctrl+C to trigger SIGINT")
while True:
	 	time.sleep(1)

输出

在执行上述程序时,您将获得以下结果 -

Press Ctrl+C to trigger SIGINT
Signal 2 received
Signal 2 received
Signal 2 received
Signal 2 received

Windows 上的信号处理

在 Windows 上,signal.signal() 函数只能处理一组有限的信号。如果尝试使用 Windows 不支持的信号,则会引发 ValueError。而且,如果信号名称未定义为 SIG* 模块级常量,将引发 AttributeError

Windows 上支持的信号如下 -

  • SIGABRT
  • SIGFPE
  • SIGILL
  • SIGINT
  • SIGSEGV
  • SIGTERM
  • SIGBREAK

处理计时器和警报

计时器和警报可用于安排在一定时间后传输信号。

我们来观察以下处理告警的示例。


import signal
import time

def handler(signum, stack):
	 	print('Alarm: ', time.ctime())

signal.signal(signal.SIGALRM, handler)
signal.alarm(2)
time.sleep(5)
for i in range(5):
	 	signal.alarm(2)
	 	time.sleep(5)
	 	print("interrupted #%d" % i)

输出

在执行上述程序时,您将获得以下结果 -

Alarm: Wed Jul 17 17:30:11 2024
Alarm: Wed Jul 17 17:30:16 2024
interrupted #0
Alarm: Wed Jul 17 17:30:21 2024
interrupted #1
Alarm: Wed Jul 17 17:30:26 2024
interrupted #2
Alarm: Wed Jul 17 17:30:31 2024
interrupted #3
Alarm: Wed Jul 17 17:30:36 2024
interrupted #4

从 Numbers 获取 Signal Names

在 Python 中没有从数字获取信号名称的简单方法。你可以使用 signal 模块来获取它的所有属性,过滤掉那些以 SIG 开头的属性,并将它们存储在字典中。

此示例创建一个字典,其中键是信号编号,值是相应的信号名称。这对于从信号的数值动态解析信号名称非常有用。


import signal

sig_items = reversed(sorted(signal.__dict__.items()))
final = dict((k, v) for v, k in sig_items if v.startswith('SIG') and not v.startswith('SIG_'))
print(final)

输出

在执行上述程序时,您将获得以下结果 -

{<Signals.SIGXFSZ: 25>: 'SIGXFSZ', <Signals.SIGXCPU: 24>: 'SIGXCPU', <Signals.SIGWINCH: 28>: 'SIGWINCH', <Signals.SIGVTALRM: 26>: 'SIGVTALRM', <Signals.SIGUSR2: 12>: 'SIGUSR2', <Signals.SIGUSR1: 10>: 'SIGUSR1', <Signals.SIGURG: 23>: 'SIGURG', <Signals.SIGTTOU: 22>: 'SIGTTOU', <Signals.SIGTTIN: 21>: 'SIGTTIN', <Signals.SIGTSTP: 20>: 'SIGTSTP', <Signals.SIGTRAP: 5>: 'SIGTRAP', <Signals.SIGTERM: 15>: 'SIGTERM', <Signals.SIGSYS: 31>: 'SIGSYS', <Signals.SIGSTOP: 19>: 'SIGSTOP', <Signals.SIGSEGV: 11>: 'SIGSEGV', <Signals.SIGRTMIN: 34>: 'SIGRTMIN', <Signals.SIGRTMAX: 64>: 'SIGRTMAX', <Signals.SIGQUIT: 3>: 'SIGQUIT', <Signals.SIGPWR: 30>: 'SIGPWR', <Signals.SIGPROF: 27>: 'SIGPROF', <Signals.SIGIO: 29>: 'SIGIO', <Signals.SIGPIPE: 13>: 'SIGPIPE', <Signals.SIGKILL: 9>: 'SIGKILL', <Signals.SIGABRT: 6>: 'SIGABRT', <Signals.SIGINT: 2>: 'SIGINT', <Signals.SIGILL: 4>: 'SIGILL', <Signals.SIGHUP: 1>: 'SIGHUP', <Signals.SIGFPE: 8>: 'SIGFPE', <Signals.SIGCONT: 18>: 'SIGCONT', <Signals.SIGCHLD: 17>: 'SIGCHLD', <Signals.SIGBUS: 7>: 'SIGBUS', <Signals.SIGALRM: 14>: 'SIGALRM'}