signal
--- 设置异步事件处理程序¶
该模块提供了在 Python 中使用信号处理程序的机制。
一般规则¶
signal.signal()
函数允许定义在接收到信号时执行的自定义处理程序。少量的默认处理程序已经设置: SIGPIPE
被忽略(因此管道和套接字上的写入错误可以报告为普通的 Python 异常)以及如果父进程没有更改 SIGINT
,则其会被翻译成 KeyboardInterrupt
异常。
一旦设置,特定信号的处理程序将保持安装,直到它被显式重置( Python 模拟 BSD 样式接口而不管底层实现),但 SIGCHLD
的处理程序除外,它遵循底层实现。
执行 Python 信号处理程序¶
Python 信号处理程序不会在低级( C )信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉 virtual machine 稍后执行相应的 Python 信号处理程序(例如在下一个 bytecode 指令)。这会导致:
捕获同步错误是没有意义的,例如
SIGFPE
或SIGSEGV
,它们是由 C 代码中的无效操作引起的。Python 将从信号处理程序返回到 C 代码,这可能会再次引发相同的信号,导致 Python 显然的挂起。 从Python 3.3开始,你可以使用faulthandler
模块来报告同步错误。纯 C 中实现的长时间运行的计算(例如在大量文本上的正则表达式匹配)可以在任意时间内不间断地运行,而不管接收到任何信号。计算完成后将调用 Python 信号处理程序。
模块内容¶
在 3.5 版更改: 信号( SIG* ),处理程序( SIG_DFL
, SIG_IGN
)和 sigmask( SIG_BLOCK
, SIG_UNBLOCK
, SIG_SETMASK
)下面列出的相关常量变成了 enums
。 getsignal()
, pthread_sigmask()
, sigpending()
和 sigwait()
函数返回人类可读的 enums
。
在 signal
模块中定义的变量是:
-
signal.
SIG_DFL
¶ 这是两种标准信号处理选项之一;它只会执行信号的默认函数。 例如,在大多数系统上,对于
SIGQUIT
的默认操作是转储核心并退出,而对于SIGCHLD
的默认操作是简单地忽略它。
-
signal.
SIG_IGN
¶ 这是另一个标准信号处理程序,它将简单地忽略给定的信号。
-
signal.
SIGFPE
¶ 浮点异常。 例如除以零。
参见
当除法或求余运算的第二个参数为零时会引发
ZeroDivisionError
。
-
signal.
SIGILL
¶ 非法指令。
-
signal.
SIGINT
¶ 来自键盘的中断 (CTRL + C)。
默认的动作是引发
KeyboardInterrupt
。
-
signal.
SIGSEGV
¶ 段错误:无效的内存引用。
-
signal.
SIGTERM
¶ 终结信号。
-
SIG*
所有信号编号都是符号化定义的。 例如,挂起信号被定义为
signal.SIGHUP
;变量的名称与 C 程序中使用的名称相同,具体见<signal.h>
。 'signal()
' 的 Unix 手册页面列出了现有的信号 (在某些系统上这是 signal(2),在其他系统中此列表则是在 signal(7) 中)。 请注意并非所有系统都会定义相同的信号名称集;只有系统所定义的名称才会由此模块来定义。
-
signal.
NSIG
¶ 比最高信号数多一。
-
signal.
ITIMER_VIRTUAL
¶ 仅在进程执行时递减间隔计时器,并在到期时发送 SIGVTALRM 。
-
signal.
ITIMER_PROF
¶ 当进程执行时以及当系统替进程执行时都会减小间隔计时器。 这个计时器与 ITIMER_VIRTUAL 相配结,通常被用于分析应用程序在用户和内核空间中花费的时间。 SIGPROF 会在超期时被发送。
-
signal.
SIG_BLOCK
¶ pthread_sigmask()
的 how 形参的一个可能的值,表明信号将会被阻塞。3.3 新版功能.
-
signal.
SIG_UNBLOCK
¶ pthread_sigmask()
的 how 形参的是个可能的值,表明信号将被解除阻塞。3.3 新版功能.
-
signal.
SIG_SETMASK
¶ pthread_sigmask()
的 how 形参的一个可能的值,表明信号掩码将要被替换。3.3 新版功能.
signal
模块定义了一个异常:
-
exception
signal.
ItimerError
¶ 作为来自下层
setitimer()
或getitimer()
实现错误的信号被引发。 如果将无效的定时器或负的时间值传给setitimer()
就导致这个错误。 此错误是OSError
的子类型。
signal
模块定义了以下函数:
-
signal.
alarm
(time)¶ 如果 time 值非零,则此函数将要求将一个
SIGALRM
信号在 time 秒内发往进程。 任何在之前排入计划的警报都会被取消(在任何时刻都只能有一个警报被排入计划)。 后续的返回值将是任何之前设置的警报被传入之前的秒数。 如果 time 值为零,则不会将任何警报排入计划,并且任何已排入计划的警报都会被取消。 如果返回值为零,则目前没有任何警报被排入计划。
-
signal.
getsignal
(signalnum)¶ 返回当前用于信号 signalnum 的信号处理程序。 返回值可以是一个 Python 可调用对象,或是特殊值
signal.SIG_IGN
,signal.SIG_DFL
或None
之一。 在这里,signal.SIG_IGN
表示信号在之前被忽略,signal.SIG_DFL
表示之前在使用默认的信号处理方式,而None
表示之前的信号处理程序未由 Python 安装。
-
signal.
strsignal
(signalnum)¶ 返回信号 signalnum 的系统描述,例如 "Interrupt", "Segmentation fault" 等等。 如果信号无法被识别则返回
None
。3.8 新版功能.
-
signal.
valid_signals
()¶ 返回本平台上的有效信号编号集。 这可能会少于
range(1, NSIG)
,如果某些信号被系统保留作为内部使用的话。3.8 新版功能.
-
signal.
pause
()¶ 使进程休眠直至接收到一个信号;然后将会调用适当的处理程序。 返回空值。
可用性: 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
。 提供 flags 参数是为了将来扩展;当前未定义旗标值。更多信息请参阅 pidfd_send_signal(2) 手册页面。
可用性: Linux 5.1+
3.9 新版功能.
-
signal.
pthread_kill
(thread_id, signalnum)¶ 将信号 signalnum 发送至与调用者在同一进程中另一线程 thread_id。 目标线程可被用于执行任何代码(Python或其它)。 但是,如果目标线程是在执行 Python 解释器,则 Python 信号处理程序将 由主解释器的主线程来执行。 因此,将信号发送给特定 Python 线程的唯一作用在于强制让一个正在运行的系统调用失败并抛出
InterruptedError
。使用
threading.get_ident()
或threading.Thread
对象的ident
属性为 thread_id 获取合适的值。如果 signalnum 为 0,则不会发送信号,但仍然会执行错误检测;这可被用来检测目标线程是否仍在运行。
引发一个 审计事件
signal.pthread_kill
,附带参数thread_id
,signalnum
。可用性: Unix。 更多信息请参见手册页面 pthread_kill(3)。
另请参阅
os.kill()
。3.3 新版功能.
-
signal.
pthread_sigmask
(how, mask)¶ 获取和/或修改调用方线程的信号掩码。 信号掩码是一组传送过程目前为调用者而阻塞的信号集。 返回旧的信号掩码作为一组信号。
该调用的行为取决于 how 的值,具体见下。
SIG_BLOCK
: 被阻塞信号集是当前集与 mask 参数的并集。SIG_UNBLOCK
: mask 中的信号会从当前已阻塞信号集中被移除。 允许尝试取消对一个非阻塞信号的阻塞。SIG_SETMASK
: 已阻塞信号集会被设为 mask 参数的值。
mask 是一个信号编号集合 (例如 {
signal.SIGINT
,signal.SIGTERM
})。 请使用valid_signals()
表示包含所有信号的完全掩码。例如,
signal.pthread_sigmask(signal.SIG_BLOCK, [])
会读取调用方线程的信号掩码。SIGKILL
和SIGSTOP
不能被阻塞。可用性: Unix。 更多信息请参见手册页面 sigprocmask(3) 和 pthread_sigmask(3)。
另请参阅
pause()
,sigpending()
和sigwait()
。3.3 新版功能.
-
signal.
setitimer
(which, seconds, interval=0.0)¶ 设置由 which 指明的给定间隔计时器 (
signal.ITIMER_REAL
,signal.ITIMER_VIRTUAL
或signal.ITIMER_PROF
之一) 在 seconds 秒 (接受浮点数值,为与alarm()
之差) 之后开始并在每 interval 秒间隔时 (如果 interval 不为零) 启动。 由 which 指明的间隔计时器可通过将 seconds 设为零来清空。当一个间隔计时器启动时,会有信号发送至进程。 所发送的具体信号取决于所使用的计时器;
signal.ITIMER_REAL
将发送SIGALRM
,signal.ITIMER_VIRTUAL
将发送SIGVTALRM
, 而signal.ITIMER_PROF
将发送SIGPROF
.原有的值会以元组: (delay, interval) 的形式被返回。
尝试传入无效的计时器将导致
ItimerError
。可用性: Unix。
-
signal.
set_wakeup_fd
(fd, *, warn_on_full_buffer=True)¶ 将唤醒文件描述符设为 fd。 当接收到信号时,会将信号编号以单个字节的形式写入 fd。 这可被其它库用来唤醒一次 poll 或 select 调用,以允许该信号被完全地处理。
原有的唤醒 fd 会被返回(或者如果未启用文件描述符唤醒则返回 -1)。 如果 fd 为 -1,文件描述符唤醒会被禁用。 如果不为 -1,则 fd 必须为非阻塞型。 需要由库来负责在重新调用 poll 或 select 之前从 fd 移除任何字节数据。
当启用线程用时,此函数只能从 主解释器的主线程 被调用;尝试从另一线程调用它将导致
ValueError
异常被引发。使用此函数有两种通常的方式。 在两种方式下,当有信号到达时你都是用 fd 来唤醒,但之后它们在确定达到的一个或多个信号 which 时存在差异。
在第一种方式下,我们从 fd 的缓冲区读取数据,这些字节值会给你信号编号。 这种方式很简单,但在少数情况下会发生问题:通常 fd 将有缓冲区空间大小限制,如果信号到达得太多且太快,缓冲区可能会爆满,有些信号可能丢失。 如果你使用此方式,则你应当设置
warn_on_full_buffer=True
,当信号丢失时这至少能将警告消息打印到 stderr。在第二种方式下,我们 只会 将唤醒 fd 用于唤醒,而忽略实际的字节值。 在此情况下,我们所关心的只有 fd 的缓冲区为空还是不为空;爆满的缓冲区完全不会导致问题。 如果你使用此方式,则你应当设置
warn_on_full_buffer=False
,这样你的用户就不会被虚假的警告消息所迷惑。在 3.5 版更改: 在 Windows 上,此函数现在也支持套接字处理。
在 3.7 版更改: 添加了
warn_on_full_buffer
形参。
-
signal.
siginterrupt
(signalnum, flag)¶ 更改系统调用重启行为:如果 flag 为
False
,系统调用将在被信号 signalnum 中断时重启,否则系统调用将被中断。 返回空值。可用性: Unix。 更多信息请参见手册页面 siginterrupt(3)。
请注意用
signal()
安装信号处理程序将重启行为重置为可通过显式调用siginterrupt()
并为给定信号的 flag 设置真值来实现中断。
-
signal.
signal
(signalnum, handler)¶ 将信号 signalnum 的处理程序设为函数 handler。 handler 可以为接受两个参数(见下)的 Python 可调用对象,或者为特殊值
signal.SIG_IGN
或signal.SIG_DFL
之一。 之前的信号处理程序将被返回(参见上文getsignal()
的描述)。 (更多信息请参阅 Unix 手册页面 signal(2)。)当启用线程用时,此函数只能从 主解释器的主线程 被调用;尝试从另一线程调用它将导致
ValueError
异常被引发。handler 将附带两个参数调用:信号编号和当前堆栈帧 (
None
或一个帧对象;有关帧对象的描述请参阅 类型层级结构描述 或者参阅inspect
模块中的属性描述)。在 Windows 上,
signal()
调用只能附带SIGABRT
,SIGFPE
,SIGILL
,SIGINT
,SIGSEGV
,SIGTERM
或SIGBREAK
。 任何其他值都将引发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 中的信号之一已经在等待调用方线程,此函数将立即返回并附带有关该信号的信息。 被传送信号的信号处理程序不会被调用。 如果该函数被某个不在 sigset 中的信号中断则会引发
InterruptedError
。返回值是一个代表
siginfo_t
结构体所包含数据的对象,具体为:si_signo
,si_code
,si_errno
,si_pid
,si_uid
,si_status
,si_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 版更改: The function is now retried with the recomputed timeout if interrupted by a signal not in sigset and the signal handler does not raise an exception (see PEP 475 for the rationale).
示例¶
Here is a minimal example program. It uses the alarm()
function to limit
the time spent waiting to open a file; this is useful if the file is for a
serial device that may not be turned on, which would normally cause the
os.open()
to hang indefinitely. The solution is to set a 5-second alarm
before opening the file; if the operation takes too long, the alarm signal will
be sent, and the handler raises an exception.
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
Note on SIGPIPE¶
Piping output of your program to tools like head(1) will
cause a SIGPIPE
signal to be sent to your process when the receiver
of its standard output closes early. This results in an exception
like BrokenPipeError: [Errno 32] Broken pipe
. To handle this
case, wrap your entry point to catch this exception as follows:
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()
Do not set SIGPIPE
's disposition to SIG_DFL
in order to avoid BrokenPipeError
. Doing that would cause
your program to exit unexpectedly also whenever any socket connection
is interrupted while your program is still writing to it.