Python 简明教程

Python - Signal Handling

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

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

  1. SIGPIPE is ignored.

  2. SIGINT 被翻译成一个键盘中断异常。

Commonly Used Signals

Python 信号处理程序在主解释器的 Python 主线程中执行,即使信号在其他线程中收到。信号不能用于线程间通信。

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

  1. SIGINT − 来自键盘的中断(Ctrl+C),它会引发键盘中断。

  2. SIGTERM − Termination signal.

  3. SIGALRM − 来自 alarm() 的定时器信号。

  4. SIGCHLD − 子进程停止或终止。

  5. SIGUSR1SIGUSR2 − 用户定义的信号。

Setting a Signal Handler

要设置一个信号处理程序,我们可以使用 signal.signal() 函数。它允许你为信号定义自定义处理程序。处理程序在显式重置之前将一直被安装,但 SIGCHLD 除外。

Example

下面是一个使用 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)

Output

通过执行以上的程序,你会得到以下结果:

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

Signal Handling on Windows

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

在 Windows 上支持的信号如下:

  1. SIGABRT

  2. SIGFPE

  3. SIGILL

  4. SIGINT

  5. SIGSEGV

  6. SIGTERM

  7. SIGBREAK

Handling Timers and Alarms

定时器和警报可用于调度在一段时间后送达信号。

Example

让我们观察处理警报的以下示例。

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)

Output

通过执行以上的程序,你会得到以下结果:

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

Getting Signal Names from Numbers

在 Python 中还没有直接的方法从数字中获取信号名称。你可以使用 signal 模块来获取它的所有属性,筛选出以 SIG 开头的属性,并将其存储在一个字典中。

Example

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

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)

Output

通过执行以上的程序,你会得到以下结果:

{<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'}