Python - 信号处理



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

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

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

常用信号

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

以下是某些常用信号及其默认操作的列表:

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

设置信号处理程序

要设置信号处理程序,我们可以使用signal.signal()函数。它允许您为信号定义自定义处理程序。处理程序保持安装状态,直到显式重置,除了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

从数字获取信号名称

在 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'}
广告