17.4. 信号 — 为异步事件设置处理程序 — Python 文档
17.4. 信号 — 为异步事件设置处理程序
该模块提供了在 Python 中使用信号处理程序的机制。 使用信号及其处理程序的一些一般规则:
- 一个特定信号的处理程序,一旦设置,将一直保持安装,直到它被显式重置(Python 模拟 BSD 风格的接口,而不管底层实现如何),除了
SIGCHLD
的处理程序,它遵循底层实现. - 没有办法暂时“阻止”来自临界区的信号(因为并非所有 Unix 风格都支持)。
- 尽管就 Python 用户而言,Python 信号处理程序是异步调用的,但它们只能发生在 Python 解释器的“原子”指令之间。 这意味着在纯 C 实现的长时间计算中到达的信号(例如大文本体上的正则表达式匹配)可能会延迟任意时间。
- 当信号在 I/O 操作期间到达时,I/O 操作可能会在信号处理程序返回后引发异常。 这取决于底层 Unix 系统关于中断系统调用的语义。
- 因为 C 信号处理程序总是返回,所以捕获像
SIGFPE
或SIGSEGV
这样的同步错误没有多大意义。 - Python 默认安装少量信号处理程序:
SIGPIPE
被忽略(所以管道和套接字上的写错误可以报告为普通的 Python 异常)并且SIGINT
被翻译成一个 [ X207X] 异常。 所有这些都可以被覆盖。 - 如果在同一程序中同时使用信号和线程,则必须小心。 同时使用信号和线程要记住的基本事情是:始终在执行的主线程中执行 signal() 操作。 任何线程都可以执行 alarm()、getsignal()、pause()、setitimer() 或 getitimer( ); 只有主线程可以设置新的信号处理程序,并且主线程将是唯一接收信号的线程(这是由 Python signal 模块强制执行的,即使底层线程实现支持向单个线程发送信号)线程)。 这意味着信号不能用作线程间通信的手段。 使用锁代替。
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。
2.7 版中的新功能。
- signal.CTRL_BREAK_EVENT
Ctrl+Break击键事件对应的信号。 此信号只能与 os.kill() 一起使用。
可用性:Windows。
2.7 版中的新功能。
- signal.NSIG
- 比最高信号数多一的数。
- signal.ITIMER_REAL
- 实时递减间隔定时器,并在到期时交付
SIGALRM
。
- signal.ITIMER_VIRTUAL
- 仅在进程执行时递减间隔计时器,并在到期时传递 SIGVTALRM。
- signal.ITIMER_PROF
- 当进程执行时和系统代表进程执行时,都会减少间隔计时器。 与 ITIMER_VIRTUAL 结合使用,该计时器通常用于分析应用程序在用户和内核空间中花费的时间。 SIGPROF 在到期时交付。
signal 模块定义了一个例外:
- exception signal.ItimerError
- 引发来自底层 setitimer() 或 getitimer() 实现的错误信号。 如果将无效的间隔计时器或负时间传递给 setitimer(),则会出现此错误。 此错误是
IOError
的子类型。
signal 模块定义了以下函数:
- signal.alarm(time)
- 如果 time 非零,该函数请求在 time 秒内向进程发送
SIGALRM
信号。 任何先前安排的闹钟都会被取消(任何时候只能安排一个闹钟)。 返回的值是任何先前设置的警报被传递之前的秒数。 如果时间为零,则不安排闹钟,取消任何安排的闹钟。 如果返回值为零,则当前没有调度警报。 (请参阅 Unix 手册页 alarm(2)。)可用性:Unix。
- signal.getsignal(signalnum)
- 返回信号 signalnum 的当前信号处理程序。 返回值可能是一个可调用的 Python 对象,也可能是特殊值 signal.SIG_IGN、signal.SIG_DFL 或 None 之一。 这里,signal.SIG_IGN 表示之前忽略了该信号,signal.SIG_DFL 表示之前使用了默认处理信号的方式,
None
表示之前的信号处理程序不是从 Python 安装的。
- signal.pause()
- 使进程休眠,直到收到信号; 然后将调用适当的处理程序。 什么都不返回。 不在 Windows 上。 (请参阅 Unix 手册页 signal(2)。)
- signal.setitimer(which, seconds[, interval])
设置由 指定的给定间隔计时器(signal.ITIMER_REAL、signal.ITIMER_VIRTUAL 或 signal.ITIMER_PROF 之一)在 之后触发 X168X]seconds(接受浮点数,不同于 alarm()),之后每隔 interval 秒。 由指定的间隔计时器,其中可以通过将秒设置为零来清除。
当间隔计时器触发时,会向进程发送信号。 发送的信号取决于所使用的定时器; signal.ITIMER_REAL 将发送
SIGALRM
,signal.ITIMER_VIRTUAL 发送SIGVTALRM
,而 signal.ITIMER_PROF9] 将发送 [X18X] 。旧值作为元组返回:(延迟,间隔)。
尝试传递无效的间隔计时器将导致 ItimerError。 可用性:Unix。
2.6 版中的新功能。
- signal.getitimer(which)
返回由 which 指定的给定间隔计时器的当前值。 可用性:Unix。
2.6 版中的新功能。
- signal.set_wakeup_fd(fd)
将唤醒 fd 设置为 fd。 当接收到一个信号时,一个
'\0'
字节被写入fd。 库可以使用它来唤醒轮询或选择调用,从而允许完全处理信号。返回旧的唤醒 fd(如果未启用文件描述符唤醒,则返回 -1)。 如果 fd 为 -1,则禁用文件描述符唤醒。 如果不是 -1,则 fd 必须是非阻塞的。 在再次调用 poll 或 select 之前,库需要从 fd 中删除任何字节。
启用线程时,该函数只能从主线程调用; 尝试从其他线程调用它会导致引发
ValueError
异常。2.6 版中的新功能。
- signal.siginterrupt(signalnum, flag)
改变系统调用重启行为:如果flag为False,系统调用会在被信号signalnum中断时重启,否则系统调用会被中断。 什么都不返回。 可用性:Unix(有关详细信息,请参阅手册页 siginterrupt(3))。
请注意,使用 signal() 安装信号处理程序将通过隐式调用
siginterrupt()
与给定信号的真实 标志 值将重启行为重置为可中断。2.6 版中的新功能。
- signal.signal(signalnum, handler)
将信号 signalnum 的处理程序设置为函数 handler。 handler 可以是带有两个参数的可调用 Python 对象(见下文),或特殊值 signal.SIG_IGN 或 signal.SIG_DFL 之一。 将返回之前的信号处理程序(参见上面 getsignal() 的描述)。 (请参阅 Unix 手册页 signal(2)。)
启用线程时,该函数只能从主线程调用; 尝试从其他线程调用它会导致引发
ValueError
异常。调用 handler 有两个参数:信号编号和当前堆栈帧(
None
或帧对象;有关帧对象的说明,请参阅类型中的 说明hierarchy 或查看 inspect 模块中的属性描述)。在 Windows 上,signal() 只能用
SIGABRT
、SIGFPE
、SIGILL
、SIGINT
、SIGSEGV
调用] 或SIGTERM
。 在任何其他情况下都会引发ValueError
。
17.4.1. 例子
这是一个最小的示例程序。 它使用 alarm() 函数来限制等待打开文件所花费的时间; 如果文件用于可能未打开的串行设备,则这很有用,这通常会导致 os.open() 无限期挂起。 解决办法是在打开文件前设置5秒闹钟; 如果操作时间过长,将发送警报信号,处理程序引发异常。
import signal, os
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise IOError("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