该模块为在Python中的使用信号处理句柄提供支持。下面是一些使用信号和他们的句柄时需要注意的事项:
- 除了信号 SIGCHLD 的句柄遵从底层的实现外,专门针对一个信号的句柄一旦设置,除非被明确地重置,会保持被设置的状态。(Python 模拟 BSD 风格的界面,而不论底层的实现)。
- 不能从 critical sections 设法临时阻止信号,因为Unix的风格并不支持这么做。
- 尽管从 Python 用户的角度,Python 的信号句柄被称为异步的,他们也只能出现在 Python 解释器的“原子”指令之间。也就是说比如在以纯 C 书写的长运算正在进行中有信号到达,那这个信号可能会被延迟任意长的时间。
- 当信号在 I/O 操作过程中到来时, I/O 操作可能会在信号句柄返回后抛出异常。这依赖于底层 Unix 关于被打断的系统调用的语义。
- 因为 C 的信号句柄总有返回值,捕获同步的错误比如 SIGFPE 或 SIGSEGV 的意义不大。
- Python 会默认设置一些信号句柄: SIGPIPE 被忽略 (因此管道和套接字上的写错误可以像普通的 Python 异常一样报告) , SIGINT 被转成一个 KeyboardInterrupt 异常。不过这些都是可以覆写的。
- 同一段程序中如果既有线程又有信号,那么就要小心了:总是在执行的主线程中执行 signal() 操作。任何一个 thread 都可以执行 alarm(), getsignal(), pause(),setitimer() 或 getitimer(); 只有 main thread 才能设置一个新的信号句柄,而且只有 main thread 才能够接收信号 (Python 的 signal 模块就是这么规定的,不论底层的线程实现是否允许向独立的线程发送信号)。这意味着信号不能被用来充当线程间的通信手段,在Python中请使用锁。
signal 模块中的变量:
- signal.SIG_DFL
- 这是两个标准信号句柄选项之一,将会执行为信号设置的默认函数。例如,在大多数的系统上对于 SIGQUIT 的默认动作是 dump 内核然后退出,而对于 SIGCHLD 的默认动作是直接无视它。
- signal.SIG_IGN
- 这是两个标准信号句柄选项这种的另一个,直接忽略指定的信号。
- SIG*
- 所有的信号数都以符号常量的形式定义,例如,挂起信号被定义为 signal.SIGHUP,变量名与 C 程序中使用的相同,可以在 <signal.h> 头文件中找到。Unix 对应 ‘signal()‘ 的 man 页面列出了所有已经定义的信号,(有些系统上是 signal(2),有的系统上是 signal(7))。注意不是所有的系统上都定义相同的信号名。
- signal.CTRL_C_EVENT
- 对应于 CTRL+C 击键事件,该信号只能与 os.kill() 一起使用。
- 适用于: Windows.
- Python 2.7 引入
- signal.CTRL_BREAK_EVENT
- 对应 CTRL+BREAK 击键操作,该信号只能与 os.kill() 一起使用。
- 适用于: Windows.
- Python 2.7 引入
- signal.NSIG
- 刚好比最大信号的值大1。
- signal.ITIMER_REAL
- Decrements interval timer in real time, and delivers SIGALRM upon expiration.
- signal.ITIMER_VIRTUAL
- Decrements interval timer only when the process is executing, and delivers SIGVTALRM upon expiration.
- signal.ITIMER_PROF
- Decrements interval timer both when the process executes and when the system is executing on behalf of the process. Coupled with ITIMER_VIRTUAL, this timer is usually used to profile the time spent by the application in user and kernel space. SIGPROF is delivered upon expiration.
-
signal 模块的异常
- signal.ItimerError
- 从底层 setitimer() 或 getitimer() 抛出,用来标志一个错误。该异常在一个无效的计时器或负的时间被传入 setitimer() 时抛出,是 IOError 的子类。
signal 模块的函数
signal.alarm(time)
如果参数 time 是非零的,这个函数会请求一个 SIGALRM 信号在 time 秒内发给进程。先前已经调度的 alarm 全部取消 (一次只能调度一个 alarm )。 返回值是先前设置的 alarm 还剩余的秒数。如果 time 是零,不会调度任何 alarm ,而且会取消一切已经调度的 alarm 。如果返回值是零,说明当前没有被调度的 alarm 。(查看 Unix man 手册 alarm(2).)
适用于: Unix
signal.getsignal(signalnum)
返回值是对应于信号 signalnum 的信号句柄,可能是一个Python 可调用对象,或者是 signal.SIG_IGN,signal.SIG_DFL 或 None 中的一个。这里 signal.SIG_IGN 意味着该信号此前被忽略, signal.SIG_DFL 意味着先前使用默认方式处理这个信号, None 意味着先前的信号句柄没有设置。
signal.pause()
造成进程睡眠直到信号到来,合适的句柄到时会被调用,没有返回值,不支持Windows。(查看 Unix man 手册 signal(2))。
signal.setitimer(which, seconds[, interval])
将参数 which 指定的计时器 ( signal.ITIMER_REAL,signal.ITIMER_VIRTUAL 或 signal.ITIMER_PROF 中的一个) 设置为参数 seconds 秒后触发(float 型的也可,不同于 alarm()) ,然后每隔 interval 秒后触发一次。通过将 seconds 设置为0可以清除 which 指定的计时器。
当一个计时器触犯时,一个信号被发送给进程,这个被发送的信号取决于使用的定时器, signal.ITIMER_REAL 会发送 SIGALRM , signal.ITIMER_VIRTUAL 发送 SIGVTALRM, signal.ITIMER_PROF 发送 SIGPROF。
旧值被作为元组返回:(delay, interval)。
传入一个无效的定时器会造成一个 ItimerError
适用于: Unix.
Python 2.6 引入
signal.getitimer(which)
返回参数 which 指定的定时器的当前值。
适用于: Unix.
Python 2.6 引入
signal.set_wakeup_fd(fd)
将唤醒文件描述字设置为参数 fd。当一个信号被接收时,一个 ‘\0‘ 字节被写入到指定的 fd 中。 可以被一个一个库用来唤醒一个 poll 或 select 调用,允许信号被完全处理。
返回旧的文件描述字,参数 fd 必须是非阻塞的,是否在再次调用 poll 或 select 之前清除字节记录取决于具体的库。
当允许线程时,该函数只能被主线程调用,其他的线程调用它将会抛出 ValueError 异常。
Python 2.6 引入
signal.siginterrupt(signalnum, flag)
改变系统调用重启规则:如果 flag 是 False, 系统调用将会在被信号 signalnum 打断时重启,否则系统调用将会被打断。没有返回值。
适用于: Unix (查看 man 手册的 siginterrupt(3))
注意当用 signal() 设置一个信号句柄时将会通过隐式调用 siginterrupt() 重置重启行为为可打断,即对于指定的的信号,flag 为 真。
Python 2.6 引入
signal.signal(signalnum, handler)
将信号 signalnum 的句柄设置为函数 handler。 handler 可以是一个 Python 可调用对象,接受两个参数,或 signal.SIG_IGN 和 signal.SIG_DFL 中的一个。该函数将会返回之前的信号句柄,
当允许线程时,该函数只能被主线程调用,其他的线程调用它将会抛出 ValueError 异常。
调用句柄 handler 需要提供两个参数:信号的数值和当前的栈帧 (None 或一个 frame 对象;查阅 description in the type hierarchy 或 inspect 模块中的属性描述可以了解 frame 对象)。
Windows上 signal() 只能用 SIGABRT,SIGFPE,SIGILL,SIGINT,SIGSEGV 或 SIGTERM 调用。 否则会抛出 ValueError 异常。
例子
这里有一个例子,使用 alarm() 函数来限制等待打开一个文件的时间,当文件是为一个不一定会被打开的连续设备准备时很有用,这种情况通常会造成 os.open() 立刻挂起。解决办法是在开文件钱设置一个5秒的 alarm,当打开文件超时时, alarm 信号会被发送,句柄将会抛出一个异常。
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