Skip to main content

18.8. signal —设置异步事件的处理程序


此模块提供了在Python中使用信号处理程序的机制。

18.8.1. 一般规则

signal.signal() 功能允许定义在接收到信号时执行的自定义处理程序。安装少量的默认处理程序:SIGPIPE 被忽略(因此管道和套接字上的写入错误可以报告为普通的Python异常),SIGINT 被转换为 KeyboardInterrupt 异常。

特定信号的处理程序一旦设置,将保持安装状态,直到显式复位(Python模拟BSD样式接口,而不考虑底层实现),但 SIGCHLD 的处理程序除外,该处理程序遵循基础实现。

18.8.1.1. 执行Python信号处理程序

Python信号处理程序不在低级(C)信号处理程序内部执行。相反,低级信号处理程序设置一个标志,该标志告诉 virtual machine 在稍后的点(例如在下一个 bytecode 指令处)执行相应的Python信号处理程序。这有后果:

  • 捕获由C代码中的无效操作引起的同步错误(如 SIGFPESIGSEGV)没有什么意义。 Python将从信号处理程序返回到C代码,这可能会再次提高相同的信号,导致Python显然挂起。从Python 3.3开始,您可以使用 faulthandler 模块报告同步错误。

  • 完全在C中实现的长时间运行的计算(例如对大量文本的正则表达式匹配)可以不间断地运行任意时间量,而不管接收到什么信号。 Python信号处理程序将在计算完成时调用。

18.8.1.2. 信号和线程

Python信号处理程序总是在主Python线程中执行,即使信号在另一个线程中被接收。这意味着信号不能用作线程间通信的手段。您可以使用来自 threading 模块的同步原语。

此外,只有主线程允许设置一个新的信号处理程序。

18.8.2. 模块内容

在 3.5 版更改: 下面列出的信号(SIG*),处理程序(SIG_DFLSIG_IGN)和sigmask(SIG_BLOCKSIG_UNBLOCKSIG_SETMASK)相关常数变为 enumsgetsignal()pthread_sigmask()sigpending()sigwait() 函数返回人类可读的 enums

signal 模块中定义的变量是:

signal.SIG_DFL

这是两个标准信号处理选项之一;它将简单地执行信号的默认功能。例如,在大多数系统上,SIGQUIT 的默认操作是转储核心和退出,而 SIGCHLD 的默认操作是简单地忽略它。

signal.SIG_IGN

这是另一个标准信号处理程序,它将简单地忽略给定的信号。

SIG*

所有信号编号都用符号定义。例如,挂机信号被定义为 signal.SIGHUP;变量名称与C程序中使用的名称相同,如 <signal.h> 中所示。 “ signal() ”的Unix手册页列出了现有的信号(在某些系统上,这是 signal(2),其他系统上的列表是 signal(7))。请注意,并非所有系统都定义相同的信号名称集;只有由系统定义的那些名称由此模块定义。

signal.CTRL_C_EVENT

该信号对应于 Ctrl+C 键击事件。此信号只能与 os.kill() 一起使用。

可用性:Windows。

3.2 新版功能.

signal.CTRL_BREAK_EVENT

该信号对应于 Ctrl+Break 键击事件。此信号只能与 os.kill() 一起使用。

可用性:Windows。

3.2 新版功能.

signal.NSIG

一个多于最高信号数的数。

signal.ITIMER_REAL

实时递减间隔定时器,并在到期时传递 SIGALRM

signal.ITIMER_VIRTUAL

仅在进程正在执行时减少间隔定时器,并在到期时传递SIGVTALRM。

signal.ITIMER_PROF

在进程执行时以及系统代表进程执行时,递减间隔定时器。与ITIMER_VIRTUAL相耦合,此定时器通常用于配置应用程序在用户和内核空间中花费的时间。 SIGPROF在到期时交付。

signal.SIG_BLOCK

how 参数到 pthread_sigmask() 的可能值,表示信号将被阻塞。

3.3 新版功能.

signal.SIG_UNBLOCK

how 参数到 pthread_sigmask() 的可能值,指示信号将被解除阻塞。

3.3 新版功能.

signal.SIG_SETMASK

how 参数到 pthread_sigmask() 的可能值,表示要替换信号掩码。

3.3 新版功能.

signal 模块定义了一个例外:

exception signal.ItimerError

被提出以从底层 setitimer()getitimer() 实现发出错误信号。如果向 setitimer() 传递了无效的间隔定时器或负时间,则发生此错误。此错误是 OSError 的子类型。

3.3 新版功能: 此错误以前是 IOError 的子类型,现在是 OSError 的别名。

signal 模块定义以下功能:

signal.alarm(time)

如果 time 为非零,则此函数请求在 time 秒内将 SIGALRM 信号发送到进程。任何先前安排的警报被取消(只能在任何时间安排一个警报)。返回的值是之前设置的任何警报发送之前的秒数。如果 time 为零,则不计划报警,并且任何预定的报警被取消。如果返回值为零,则当前未调度报警。 (参见Unix手册页 alarm(2)。)可用性:Unix。

signal.getsignal(signalnum)

返回信号 signalnum 的当前信号处理程序。返回的值可以是可调用的Python对象,或特殊值 signal.SIG_IGNsignal.SIG_DFLNone 之一。这里,signal.SIG_IGN 意味着信号先前被忽略,signal.SIG_DFL 意味着处理信号的默认方式先前正在使用,None 意味着先前的信号处理程序没有从Python安装。

signal.pause()

使该过程睡眠直到接收到信号;然后将调用相应的处理程序。不返回任何内容。不在Windows上。 (参见Unix手册页 signal(2)。)

也参见 sigwait()sigwaitinfo()sigtimedwait()sigpending()

signal.pthread_kill(thread_id, signalnum)

发送信号 signalnum 到线程 thread_id,另一个线程在与调用者相同的进程中。目标线程可以执行任何代码(Python或不是)。但是,如果目标线程正在执行Python解释器,Python信号处理程序将是 由主线程执行。因此,向特定Python线程发送信号的唯一点是强制运行系统调用使用 InterruptedError 失败。

使用 threading.get_ident()threading.Thread 对象的 ident 属性为 thread_id 获取合适的值。

如果 signalnum 为0,则不发送信号,但仍然执行错误检查;这可以用于检查目标线程是否仍在运行。

可用性:Unix(有关详细信息,请参阅手册页 pthread_kill(3))。

参见 os.kill()

3.3 新版功能.

signal.pthread_sigmask(how, mask)

获取和/或更改调用线程的信号掩码。信号掩码是当前为呼叫者阻止递送的信号集合。将旧信号掩码作为一组信号返回。

调用的行为取决于 how 的值,如下所示。

  • SIG_BLOCK:阻塞信号的集合是当前集合和 mask 自变量的并集。

  • SIG_UNBLOCKmask 中的信号从当前组的阻塞信号中删除。允许尝试解除阻塞未被阻塞的信号。

  • SIG_SETMASK:阻塞信号集合设置为 mask 自变量。

mask 是一组信号编号(例如{signal.SIGINTsignal.SIGTERM})。使用 range(1, signal.NSIG) 作为包括所有信号的完整掩码。

例如,signal.pthread_sigmask(signal.SIG_BLOCK, []) 读取调用线程的信号掩码。

可用性:Unix。有关更多信息,请参阅手册页 sigprocmask(3)pthread_sigmask(3)

参见 pause()sigpending()sigwait()

3.3 新版功能.

signal.setitimer(which, seconds[, interval])

将由 which 指定的给定间隔定时器(signal.ITIMER_REALsignal.ITIMER_VIRTUALsignal.ITIMER_PROF 之一)设置为在 seconds (接受浮动,与 alarm() 不同)之后并在每个 interval 秒之后触发。由 which 指定的间隔定时器可以通过将秒设置为零来清除。

当间隔定时器触发时,向处理发送信号。发送的信号取决于正在使用的定时器; signal.ITIMER_REAL 将交付 SIGALRMsignal.ITIMER_VIRTUAL 发送 SIGVTALRMsignal.ITIMER_PROF 将交付 SIGPROF

旧值作为元组返回:(delay,interval)。

尝试传递无效间隔定时器将导致 ItimerError。可用性:Unix。

signal.getitimer(which)

返回由 which 指定的给定间隔定时器的当前值。可用性:Unix。

signal.set_wakeup_fd(fd)

将唤醒文件描述符设置为 fd。当接收到信号时,信号号作为单个字节写入fd。这可以由库用来唤醒轮询或选择调用,从而允许完全处理信号。

返回旧的唤醒fd。 fd 必须是非阻塞的。在调用poll或者再次选择之前,由库来删除任何字节。

使用例如 struct.unpack('%uB' % len(data), data) 来解码信号编号列表。

当启用线程时,此函数只能从主线程调用;尝试从其他线程调用它将导致引发 ValueError 异常。

在 3.5 版更改: 在Windows上,此函数现在还支持套接字句柄。

signal.siginterrupt(signalnum, flag)

更改系统呼叫重启行为:如果 flagFalse,系统呼叫将在由信号 signalnum 中断时重新启动,否则系统呼叫将中断。不返回任何内容。可用性:Unix(有关更多信息,请参见手册页 siginterrupt(3))。

请注意,使用 signal() 安装信号处理程序将通过使用给定信号的真 flag 值隐式调用 siginterrupt() 来将重新启动行为重置为可中断。

signal.signal(signalnum, handler)

将信号 signalnum 的处理程序设置为函数 handlerhandler 可以是一个可调用的Python对象,它接受两个参数(见下文),或者一个特殊值 signal.SIG_IGNsignal.SIG_DFL。将返回先前的信号处理程序(参见上面的 getsignal() 的描述)。 (参见Unix手册页 signal(2)。)

当启用线程时,此函数只能从主线程调用;尝试从其他线程调用它将导致引发 ValueError 异常。

handler 使用两个参数调用:信号编号和当前堆栈帧(None 或帧对象;有关帧对象的描述,请参阅 描述在类型层次结构中 或参见 inspect 模块中的属性描述)。

在Windows上,signal() 只能使用 SIGABRTSIGFPESIGILLSIGINTSIGSEGVSIGTERMSIGBREAK 调用。 ValueError 将在任何其他情况下提出。请注意,并非所有系统都定义相同的信号名称集;如果信号名称未定义为 SIG* 模块级常数,则将产生 AttributeError

signal.sigpending()

检查等待传递到调用线程的信号集合(即,在阻塞时已经产生的信号)。返回待处理信号的集合。

可用性:Unix(有关详细信息,请参阅手册页 sigpending(2))。

参见 pause()pthread_sigmask()sigwait()

3.3 新版功能.

signal.sigwait(sigset)

暂停执行调用线程,直到传递在信号集 sigset 中指定的信号之一。该函数接受信号(将其从待处理的信号列表中删除),并返回信号编号。

可用性:Unix(有关详细信息,请参阅手册页 sigwait(3))。

也参见 pause()pthread_sigmask()sigpending()sigwaitinfo()sigtimedwait()

3.3 新版功能.

signal.sigwaitinfo(sigset)

暂停执行调用线程,直到传递在信号集 sigset 中指定的信号之一。该函数接受信号并将其从待处理的信号列表中删除。如果 sigset 中的信号之一已经等待调用线程,则函数将立即返回关于该信号的信息。对于传递的信号,不调用信号处理程序。如果 InterruptedError 被不在 sigset 中的信号中断,该函数将产生 InterruptedError

返回值是表示 siginfo_t 结构中包含的数据的对象,即:si_signosi_codesi_errnosi_pidsi_uidsi_statussi_band

可用性:Unix(有关详细信息,请参阅手册页 sigwaitinfo(2))。

参见 pause()sigwait()sigtimedwait()

3.3 新版功能.

在 3.5 版更改: 如果被不在 sigset 中的信号中断,并且信号处理程序不引发异常,则此函数现在重试(参见 PEP 475 的原理)。

signal.sigtimedwait(sigset, timeout)

sigwaitinfo(),但是需要一个额外的 timeout 参数指定超时。如果 timeout 被指定为 0,则执行轮询。如果发生超时,返回 None

可用性:Unix(有关详细信息,请参阅手册页 sigtimedwait(2))。

参见 pause()sigwait()sigwaitinfo()

3.3 新版功能.

在 3.5 版更改: 如果被不在 sigset 中的信号中断,并且信号处理程序不引发异常,则现在使用重新计算的 timeout 重试该函数(参见 PEP 475 的原理)。

18.8.3. 例

这里是一个最小的示例程序。它使用 alarm() 功能来限制等待打开文件所花费的时间;如果文件是用于可能未打开的串行设备,这通常会导致 os.open() 无限期挂起,这是非常有用的。解决方法是在打开文件之前设置5秒报警;如果操作过长,将发送报警信号,并且处理程序引发异常。

import signal, os

def handler(signum, frame):
    print('Signal handler called with signal', signum)
    raise OSError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm