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


该模块提供了在 Python 中使用 signal 处理程序的机制。

General rules

signal.signal()函数允许定义自定义处理程序,以便在收到 signal 时执行。安装了少量默认处理程序:忽略SIGPIPE(因此可以将管道和套接字上的写入错误报告为普通的 Python 异常),并且如果父进程未将SIGINT转换为KeyboardInterrupt异常。

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

Pythonsignal 处理程序的执行

Pythonsignal 处理程序不会在底层(C)signal 处理程序中执行。相反,低级 signal 处理程序设置一个标志,该标志告诉virtual machine在以后的位置(例如,在下一条bytecode指令处)执行相应的 Pythonsignal 处理程序。结果是:

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

  • 纯粹以 C 语言实现的长时间运行的计算(例如,大文本正文上的正则表达式匹配)可以在任意时间内连续运行,而不管收到任何 signal。计算完成后,将调用 Pythonsignal 处理程序。

signal 和线程

即使在另一个线程中接收到 signal,Pythonsignal 处理程序也总是在主 Python 线程中执行。这意味着 signal 不能用作线程间通信的手段。您可以改为使用threading模块中的同步 Primitives。

此外,仅允许主线程设置新的 signal 处理程序。

Module contents

在版本 3.5 中进行了更改:将下面列出的与 signal(SIG *),处理程序(SIG_DFLSIG_IGN)和 sigmask(SIG_BLOCKSIG_UNBLOCKSIG_SETMASK)相关的常量转换为enumsgetsignal()pthread_sigmask()sigpending()sigwait()函数返回可读的enums

signal模块中定义的变量是:

  • signal. SIG_DFL

    • 这是两个标准 signal 处理选项之一。它只会执行 signal 的默认Function。例如,在大多数系统上,SIGQUIT的默认操作是转储核心并退出,而SIGCHLD的默认操作是简单地忽略它。
  • signal. SIG_IGN

    • 这是另一个标准 signal 处理程序,它将简单地忽略给定的 signal。
  • signal. SIGABRT

  • signal. SIGALRM

    • 来自* alarm(2) *的计时器 signal。

Availability: Unix.

  • signal. SIGBREAK
    • 键盘break(CTRL BREAK)。

Availability: Windows.

  • signal. SIGBUS
    • 总线错误(错误的内存访问)。

Availability: Unix.

  • signal. SIGCHLD
    • 子进程停止或终止。

Availability: Windows.

  • signal. SIGCLD

  • signal. SIGCONT

    • 如果当前已停止,则 continue 该过程

Availability: Unix.

  • signal. SIGFPE
    • 浮点异常。例如,除以零。

See also

当除法或模运算的第二个参数为零时,将引发ZeroDivisionError

  • signal. SIGHUP
    • 在控制终端上检测到挂断或控制进程终止。

Availability: Unix.

  • signal. SIGILL

    • Illegal instruction.
  • signal. SIGINT

    • 键盘break(CTRL C)。

默认操作是加注KeyboardInterrupt

  • signal. SIGKILL
    • Kill signal.

无法捕获,阻止或忽略它。

Availability: Unix.

  • signal. SIGPIPE
    • 管道损坏:在没有读取器的情况下写入管道。

默认操作是忽略 signal。

Availability: Unix.

  • signal. SIGSEGV

    • 分段错误:无效的内存引用。
  • signal. SIGTERM

    • Termination signal.
  • signal. SIGUSR1

    • 用户定义的 signal1.

Availability: Unix.

  • signal. SIGUSR2
    • 用户定义的 signal2.

Availability: Unix.

  • signal. SIGWINCH
    • 窗口调整大小 signal。

Availability: Unix.

  • SIG*

    • 所有 signal 编号均以符号方式定义。例如,挂断 signal 定义为signal.SIGHUP;变量名与<signal.h>中的 C 程序中使用的名称相同。 signal()的 Unix 手册页列出了现有的 signal(在某些系统上是* signal(2) ,在其他系统上,列表在 signal(7) *中)。请注意,并非所有系统都定义相同的 signal 名称集。此模块仅定义系统定义的名称。
  • signal. CTRL_C_EVENT

    • 与 Ctrl C 按键事件相对应的 signal。该 signal 只能与os.kill()一起使用。

Availability: Windows.

3.2 版中的新Function。

  • signal. CTRL_BREAK_EVENT
    • 与 Ctrl Break 击键事件相对应的 signal。该 signal 只能与os.kill()一起使用。

Availability: Windows.

3.2 版中的新Function。

  • signal. NSIG

    • 比最高 signal 编号多一。
  • signal. ITIMER_REAL

    • 实时减少间隔计时器,并在到期时发送SIGALRM
  • signal. ITIMER_VIRTUAL

    • 仅在进程执行时才减小间隔计时器,并在到期时传递 SIGVTALRM。
  • signal. ITIMER_PROF

    • 在过程执行时以及系统代表过程执行时,都减小间隔计时器。与 ITIMER_VIRTUAL 结合使用时,该计时器通常用于分析应用程序在用户和内核空间中花费的时间。 SIGPROF 在到期时交付。
  • signal. SIG_BLOCK

版本 3.3 中的新Function。

  • signal. SIG_UNBLOCK

版本 3.3 中的新Function。

  • signal. SIG_SETMASK
    • pthread_sigmask()的* how *参数的可能值,指示 signal 掩码将被替换。

版本 3.3 中的新Function。

signal模块定义了一个 exception:

  • exception signal. ItimerError

版本 3.3 中的新增Function:此错误曾经是IOError的子类型,现在是OSError的别名。

signal模块定义以下Function:

  • signal. alarm(时间)
    • 如果* time 不为零,则此函数要求在 time 秒内将SIGALRMsignal 发送到进程。任何先前计划的警报都会被取消(任何时候都只能计划一个警报)。然后,返回值是在传递任何先前设置的警报之前的秒数。如果 time *为零,则不会安排任何警报,并且任何计划的警报都会被取消。如果返回值为零,则当前未计划任何警报。

Availability:Unix。有关更多信息,请参见手册页* alarm(2) *。

  • signal. getsignal(* signalnum *)

    • 返回 signal* signalnum *的当前 signal 处理程序。返回的值可以是可调用的 Python 对象,也可以是特殊值signal.SIG_IGNsignal.SIG_DFLNone之一。在这里,signal.SIG_IGN表示先前已忽略 signal,signal.SIG_DFL表示先前已使用默认的 signal 处理方式,None表示未从 Python 安装以前的 signal 处理程序。
  • signal. strsignal(* signalnum *)

    • 返回 signal* signalnum *的系统描述,例如“ Interrupt”,“ Segmentation fault”等。如果未识别到 signal,则返回None

3.8 版的新Function。

  • signal. valid_signals ( )
    • 返回此平台上的有效 signal 编号集。如果系统保留了一些 signal 供内部使用,则该值可以小于range(1, NSIG)

3.8 版的新Function。

  • signal. pause ( )
    • 使过程进入休眠状态,直到收到 signal 为止;然后将调用适当的处理程序。不返回任何内容。

Availability:Unix。有关更多信息,请参见手册页* signal(2) *。

另请参见sigwait()sigwaitinfo()sigtimedwait()sigpending()

  • signal. raise_signal(* signum *)
    • 向呼叫过程发送 signal。不返回任何内容。

3.8 版的新Function。

  • signal. pthread_kill(* thread_id signalnum *)
    • 将 signal* signalnum 发送到线程 thread_id *,该线程与调用方的进程相同。目标线程可以执行任何代码(是否使用 Python)。但是,如果目标线程正在执行 Python 解释器,则 Pythonsignal 处理程序将为由主线程执行。因此,向特定的 Python 线程发送 signal 的唯一目的是强制正在运行的系统调用以InterruptedError失败。

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

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

用参数thread_idsignalnum引发auditing event signal.pthread_kill

Availability:Unix。有关更多信息,请参见手册页* pthread_kill(3) *。

另请参见os.kill()

版本 3.3 中的新Function。

  • signal. pthread_sigmask(方式遮罩)
    • 获取和/或更改调用线程的 signal 掩码。signal 掩码是一组 signal,其当前已被呼叫者阻止。将旧的 signal 掩码作为一组 signal 返回。

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

  • SIG_BLOCK:被阻止 signal 的集合是当前集合和* mask *参数的并集。

  • SIG_UNBLOCK:* mask *中的 signal 已从当前阻塞 signal 集中删除。允许try解锁未被阻止的 signal。

  • SIG_SETMASK:被阻止的 signal 集被设置为* mask *参数。

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

SIGKILLSIGSTOP无法被阻止。

Availability:Unix。有关更多信息,请参见手册* sigprocmask(3) pthread_sigmask(3) *。

另请参见pause()sigpending()sigwait()

版本 3.3 中的新Function。

  • signal. setitimer(* which seconds interval = 0.0 *)

当间隔计时器触发时,signal 将发送到该进程。发送的 signal 取决于所使用的计时器。 signal.ITIMER_REAL将传送SIGALRMsignal.ITIMER_VIRTUAL将传送SIGVTALRM,而signal.ITIMER_PROF将传送SIGPROF

旧值以 Tuples 形式返回:(延迟,间隔)。

trypass无效的间隔计时器将导致ItimerError

Availability: Unix.

  • signal. getitimer(* which *)
    • 返回由* which *指定的给定间隔计时器的当前值。

Availability: Unix.

  • signal. set_wakeup_fd(* fd **,* warn_on_full_buffer = True *)
    • 将唤醒文件 Descriptors 设置为* fd *。接收到 signal 后,signal 编号将作为单个字节写入 fd。库可以使用它来唤醒轮询或选择调用,从而使 signal 得到充分处理。

返回旧的唤醒 fd(如果未启用文件 Descriptors 唤醒,则返回-1)。如果* fd 为-1,则禁用文件 Descriptors 唤醒。如果不为-1,则 fd 必须是非阻塞的。在调用 poll 或再次选择之前,库必须从 fd *中删除任何字节。

启用线程后,只能从主线程调用此函数;try从其他线程调用它会引发ValueError异常。

有两种使用此Function的常用方法。在这两种方法中,您都使用 fd 在 signal 到达时唤醒,但是它们在确定哪个signal 已经到达时的方式有所不同。

在第一种方法中,我们从 fd 的缓冲区中读取数据,字节值为您提供 signal 编号。这很简单,但在极少数情况下可能会出现问题:通常,fd 的缓冲区空间有限,并且如果太多 signal 到达得太快,则缓冲区可能变满,并且某些 signal 可能会丢失。如果使用此方法,则应设置warn_on_full_buffer=True,这将在 signal 丢失时至少导致向 stderr 打印警告。

在第二种方法中,我们仅使用 wakeup fd * only *进行唤醒,而忽略实际的字节值。在这种情况下,我们关心的只是 fd 的缓冲区是空还是非空。完整的缓冲区根本不表示问题。如果使用此方法,则应设置warn_on_full_buffer=False,以使用户不会被虚假警告消息所迷惑。

在版本 3.5 中进行了更改:在 Windows 上,该函数现在还支持套接字句柄。

在 3.7 版中进行了更改:添加了warn_on_full_buffer参数。

  • signal. siginterrupt(* signalnum flag *)
    • 更改系统调用重新启动行为:如果* flag False,则在被 signal signalnum *break时,系统调用将重新启动,否则系统调用将被break。不返回任何内容。

Availability:Unix。有关更多信息,请参见手册页* siginterrupt(3) *。

请注意,使用signal()安装 signal 处理程序将pass为给定 signal 隐式调用具有真实* flag *值的siginterrupt()将重启行为重置为可break的。

  • signal. signal(* signalnum handler *)
    • 将 signal* signalnum 的处理程序设置为函数 handler *。 * handler 可以是带有两个参数(请参见下文)或特殊值signal.SIG_IGNsignal.SIG_DFL之一的可调用 Python 对象。将返回之前的 signal 处理程序(请参见上面的getsignal()的描述)。 (有关更多信息,请参见 Unix 手册页 signal(2) *。)

启用线程后,只能从主线程调用此函数;try从其他线程调用它会引发ValueError异常。

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

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

  • signal. sigpending ( )
    • 检查待传递给调用线程的 signal 集(即在阻塞时发出的 signal)。返回未决 signal 集。

Availability:Unix。有关更多信息,请参见手册页* sigpending(2) *。

另请参见pause()pthread_sigmask()sigwait()

版本 3.3 中的新Function。

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

Availability:Unix。有关更多信息,请参见手册页* sigwait(3) *。

另请参见pause()pthread_sigmask()sigpending()sigwaitinfo()sigtimedwait()

版本 3.3 中的新Function。

  • signal. sigwaitinfo(* sigset *)
    • 暂停调用线程的执行,直到传递 signal 集* sigset 中指定的 signal 之一为止。该函数接受 signal 并将其从挂起的 signal 列表中删除。如果 sigset 中的 signal 之一已在 await 调用线程处理,则该函数将立即返回有关该 signal 的信息。不会为传递的 signal 调用 signal 处理程序。如果该 signal 被不在 sigset *中的 signal break,则该函数将引发InterruptedError

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

Availability:Unix。有关更多信息,请参见手册页* sigwaitinfo(2) *。

另请参见pause()sigwait()sigtimedwait()

版本 3.3 中的新Function。

在版本 3.5 中进行了更改:现在,如果被不在* sigset *中的 signal break并且该 signal 处理程序没有引发异常(请参阅 PEP 475),现在可以重试该函数。

  • signal. sigtimedwait(* sigset timeout *)
    • 类似于sigwaitinfo(),但是需要额外的* timeout 参数来指定超时。如果将 timeout *指定为0,则执行轮询。如果发生超时,则返回None

Availability:Unix。有关更多信息,请参见手册页* sigtimedwait(2) *。

另请参见pause()sigwait()sigwaitinfo()

版本 3.3 中的新Function。

在版本 3.5 中进行了更改:现在,如果被不在* sigset 中的 signal break并且 signal 处理程序没有引发异常,则该函数将使用重新计算的 timeout *重试(signal 原理请参见 PEP 475)。

Example

这是一个最小的示例程序。它使用alarm()函数来限制 await 打开文件所花费的时间;如果该文件用于可能无法打开的串行设备,这通常会导致os.open()无限期挂起,则这很有用。解决的办法是在打开文件之前设置 5 秒警报。如果操作时间太长,则会发送警报 signal,并且处理程序会引发异常。

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

关于 SIGPIPE 的注意事项

当将其标准输出的接收器提早关闭时,将程序的输出管道输出到* head(1) *之类的工具将导致SIGPIPEsignal 发送到您的进程。这会导致类似BrokenPipeError: [Errno 32] Broken pipe的异常。要处理这种情况,请包装您的入口点以捕获此异常,如下所示:

import os
import sys

def main():
    try:
        # simulate large output (your code replaces this loop)
        for x in range(10000):
            print("y")
        # flush output here to force SIGPIPE to be triggered
        # while inside this try block.
        sys.stdout.flush()
    except BrokenPipeError:
        # Python flushes standard streams on exit; redirect remaining output
        # to devnull to avoid another BrokenPipeError at shutdown
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)  # Python exits with error code 1 on EPIPE

if __name__ == '__main__':
    main()

不要将SIGPIPE的处置方式设置为SIG_DFL,以避免BrokenPipeError。这样做还会导致在程序仍在写入时任何套接字 Connecting 断时,程序也会意外退出。