signal ---为异步事件设置处理程序


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

一般规则

这个 signal.signal() 函数允许在接收到信号时定义要执行的自定义处理程序。安装了少量默认处理程序: SIGPIPE 被忽略(因此管道和套接字上的写入错误可以报告为普通的python异常)和 SIGINT 被翻译成 KeyboardInterrupt 如果父进程没有更改,则出现异常。

特定信号的处理程序一旦设置,将一直保持安装状态,直到显式重置为止(无论底层实现如何,python都模拟BSD样式的接口),但 SIGCHLD 在底层实现之后。

执行python信号处理程序

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

  • 捕捉同步错误是没有意义的,比如 SIGFPESIGSEGV 这是由C代码中的无效操作引起的。python将从信号处理程序返回到C代码,这可能再次引发相同的信号,导致python明显挂起。从python 3.3开始,您可以使用 faulthandler 报告同步错误的模块。

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

信号和线程

Python信号处理程序总是在主解释器的主Python线程中执行,即使信号是在另一个线程中接收到的。这意味着信号不能用作线程间通信的手段。您可以使用 threading 模块代替。

此外,只允许主解释器的主线程设置新的信号处理程序。

模块内容

在 3.5 版更改: 信号(sig*),处理程序 (SIG_DFLSIG_IGN 和Sig面具 (SIG_BLOCKSIG_UNBLOCKSIG_SETMASK )下面列出的相关常数被转换为 enums . getsignal()pthread_sigmask()sigpending()sigwait() 函数返回人可读的 enums .

中定义的变量 signal 模块包括:

signal.SIG_DFL

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

signal.SIG_IGN

这是另一个标准的信号处理程序,它只会忽略给定的信号。

signal.SIGABRT

中止信号来自 abort(3) .

signal.SIGALRM

计时器信号来自 alarm(2) .

Availability UNIX。

signal.SIGBREAK

从键盘中断(CTRL+BREAK)。

Availability :Windows。

signal.SIGBUS

总线错误(内存访问错误)。

Availability UNIX。

signal.SIGCHLD

子进程已停止或终止。

Availability UNIX。

signal.SIGCLD

别名 SIGCHLD .

signal.SIGCONT

如果进程当前已停止,请继续该进程

Availability UNIX。

signal.SIGFPE

浮点异常。例如,除以零。

参见

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

signal.SIGHUP

在控制终端检测到挂断或控制过程死亡。

Availability UNIX。

signal.SIGILL

非法指令。

signal.SIGINT

从键盘中断(CTRL+C)。

默认操作是 KeyboardInterrupt .

signal.SIGKILL

杀戮信号。

它不能被捕获、阻止或忽略。

Availability UNIX。

signal.SIGPIPE

断管:在没有读卡器的情况下写入管道。

默认操作是忽略信号。

Availability UNIX。

signal.SIGSEGV

分段错误:无效的内存引用。

signal.SIGTERM

终止信号。

signal.SIGUSR1

用户定义信号1。

Availability UNIX。

signal.SIGUSR2

用户定义信号2。

Availability UNIX。

signal.SIGWINCH

窗口大小调整信号。

Availability UNIX。

SIG*

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

signal.CTRL_C_EVENT

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

Availability :Windows。

3.2 新版功能.

signal.CTRL_BREAK_EVENT

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

Availability :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 为非零,此函数要求 SIGALRM 信号发送到进程 time 秒。任何先前计划的警报都将被取消(任何时候只能计划一个警报)。返回的值是任何先前设置的警报发出前的秒数。如果 time 为零,不计划任何警报,并且取消任何计划的警报。如果返回值为零,则当前不计划报警。

Availability :Unix系统。参见手册页 alarm(2) 更多信息。

signal.getsignal(signalnum)

返回信号的当前信号处理程序 信号符号 . 返回的值可以是可调用的python对象,也可以是特殊值之一 signal.SIG_IGNsignal.SIG_DFLNone . 在这里, signal.SIG_IGN 意味着信号之前被忽略了, signal.SIG_DFL 意味着处理信号的默认方式以前是在使用中的,并且 None 意味着之前的信号处理程序不是从python安装的。

signal.strsignal(signalnum)

返回信号的系统描述 信号符号 如“中断”、“分段错误”等返回 None 如果信号无法识别。

3.8 新版功能.

signal.valid_signals()

返回此平台上的一组有效信号号。这可能小于 range(1, NSIG) 如果系统保留一些信号供内部使用。

3.8 新版功能.

signal.pause()

使进程休眠,直到接收到信号;然后将调用相应的处理程序。不返回任何内容。

Availability :Unix系统。参见手册页 signal(2) 更多信息。

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

signal.raise_signal(signum)

向调用进程发送信号。什么也不返回。

3.8 新版功能.

signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)

发送信号 sig 到由文件描述符引用的进程 pidfd公司 . Python当前不支持 siginfo公司 参数;必须是 None . 这个 旗帜 参数是为将来的扩展提供的;当前未定义标志值。

pidfd_send_signal(2) 详细信息请参见手册页。

Availability :Linux 5.1版+

3.9 新版功能.

signal.pthread_kill(thread_id, signalnum)

发送信号 信号符号 线程 thread_id ,与调用方处于同一进程中的另一个线程。目标线程可以执行任何代码(python或not)。但是,如果目标线程正在执行python解释器,则python信号处理程序将 executed by the main thread of the main interpreter . 因此,向特定的python线程发送信号的唯一目的是强制正在运行的系统调用失败 InterruptedError .

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

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

提出一个 auditing event signal.pthread_kill 带着论据 thread_idsignalnum .

Availability :Unix系统。参见手册页 pthread_kill(3) 更多信息。

也见 os.kill() .

3.3 新版功能.

signal.pthread_sigmask(how, mask)

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

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

  • SIG_BLOCK :阻塞信号集是当前集和 mask 参数。

  • SIG_UNBLOCK :中的信号 mask 从当前的一组阻塞信号中删除。允许尝试解锁未被阻塞的信号。

  • SIG_SETMASK :阻塞信号集设置为 mask 参数。

mask is a set of signal numbers (e.g. {signal.SIGINT, signal.SIGTERM}). Use valid_signals() for a full mask including all signals.

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

SIGKILLSIGSTOP 无法阻止。

Availability :Unix系统。参见手册页 sigprocmask(2)pthread_sigmask(3) 更多信息。

也见 pause()sigpending()sigwait() .

3.3 新版功能.

signal.setitimer(which, seconds, interval=0.0)

设置给定的间隔计时器(其中一个 signal.ITIMER_REALsignal.ITIMER_VIRTUALsignal.ITIMER_PROF )由 哪一个 在…之后开火 (接受浮动,不同于 alarm() )之后每 间隔 秒(如果) 间隔 是非零)。由指定的间隔计时器 哪一个 可通过设置清除 到零。

当间隔计时器触发时,会向进程发送一个信号。发送的信号取决于使用的计时器; signal.ITIMER_REAL 将交付 SIGALRMsignal.ITIMER_VIRTUAL 发送 SIGVTALRMsignal.ITIMER_PROF 将交付 SIGPROF .

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

试图传递无效的间隔计时器将导致 ItimerError .

Availability UNIX。

signal.getitimer(which)

返回由指定的给定间隔计时器的当前值 哪一个 .

Availability UNIX。

signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)

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

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

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

使用此函数有两种常见方法。在这两种方法中,当信号到达时,使用fd来唤醒,但是它们在确定方法上有所不同 哪一个 信号已到达。

在第一种方法中,我们从fd的缓冲区中读取数据,字节值给出信号号。这很简单,但在极少数情况下会遇到一个问题:一般情况下,fd会有有限的缓冲空间,如果太多的信号到达得太快,那么缓冲空间可能会变满,一些信号可能会丢失。如果使用此方法,则应设置 warn_on_full_buffer=True 当信号丢失时,至少会导致将警告打印到stderr。

在第二种方法中,我们使用唤醒fd only 对于唤醒,忽略实际字节值。在这种情况下,我们只关心fd的缓冲区是空的还是非空的;一个完整的缓冲区根本不表示有问题。如果使用此方法,则应设置 warn_on_full_buffer=False ,这样您的用户就不会被虚假的警告消息所混淆。

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

在 3.7 版更改: 补充 warn_on_full_buffer 参数。

signal.siginterrupt(signalnum, flag)

更改系统调用重新启动行为:如果 flagFalse ,系统调用将在被信号中断时重新启动。 信号符号 ,否则系统调用将中断。什么也不返回。

Availability :Unix系统。参见手册页 siginterrupt(3) 更多信息。

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

signal.signal(signalnum, handler)

设置信号处理程序 信号符号 对函数 处理程序 . 处理程序 可以是具有两个参数(见下文)或一个特殊值的可调用python对象 signal.SIG_IGNsignal.SIG_DFL . 将返回上一个信号处理程序(请参见 getsignal() 以上)。(参见Unix手册页 signal(2) 更多信息。)

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

这个 处理程序 用两个参数调用:信号号和当前堆栈帧 (None 或框架对象;有关框架对象的描述,请参见 description in the type hierarchy 或查看中的属性描述 inspect 模块)。

在Windows上, signal() 只能用调用 SIGABRTSIGFPESIGILLSIGINTSIGSEGVSIGTERMSIGBREAK . 一 ValueError 在任何其他情况下都会被引发。注意,并非所有系统都定义相同的一组信号名称;一个 AttributeError 如果信号名称未定义为 SIG* 模块级常数。

signal.sigpending()

检查等待传递到调用线程的一组信号(即,阻塞时发出的信号)。返回一组挂起的信号。

Availability :Unix系统。参见手册页 sigpending(2) 更多信息。

也见 pause()pthread_sigmask()sigwait() .

3.3 新版功能.

signal.sigwait(sigset)

暂停调用线程的执行,直到传递信号集中指定的一个信号为止 西格特 . 函数接受信号(将其从挂起的信号列表中删除),并返回信号号。

Availability :Unix系统。参见手册页 sigwait(3) 更多信息。

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

3.3 新版功能.

signal.sigwaitinfo(sigset)

暂停调用线程的执行,直到传递信号集中指定的一个信号为止 西格特 . 函数接受信号并将其从挂起的信号列表中删除。如果其中一个信号 西格特 已经为调用线程挂起,函数将立即返回有关该信号的信息。对于传递的信号,不调用信号处理程序。函数引发 InterruptedError 如果被不在 西格特 .

返回值是表示包含在 siginfo_t 结构,即: si_signosi_codesi_errnosi_pidsi_uidsi_statussi_band .

Availability :Unix系统。参见手册页 sigwaitinfo(2) 更多信息。

也见 pause()sigwait()sigtimedwait() .

3.3 新版功能.

在 3.5 版更改: 如果被不在中的信号中断,则现在重试该函数。 西格特 信号处理程序不会引发异常(请参见 PEP 475 理由)。

signal.sigtimedwait(sigset, timeout)

类似于 sigwaitinfo() ,但需要额外的 timeout 指定超时的参数。如果 timeout 指定为 0 ,执行轮询。返回 None 如果发生超时。

Availability :Unix系统。参见手册页 sigtimedwait(2) 更多信息。

也见 pause()sigwait()sigwaitinfo() .

3.3 新版功能.

在 3.5 版更改: 函数现在重新计算 timeout 如果被不在的信号中断 西格特 信号处理程序不会引发异常(请参见 PEP 475 理由)。

例子

下面是一个最小的示例程序。它使用 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

关于SIGPIN的注释

将程序的输出管道连接到工具,如 head(1) 将导致 SIGPIPE 当标准输出的接收器提前关闭时发送给您的进程的信号。这会导致一个异常,比如 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 .这样做会导致程序意外退出,也会在程序仍在写入时中断任何套接字连接。