信号处理机制

Python信号处理机制

本篇的信号处理机制不是指Python的signal模块的使用,而是指Python解释器本身如何处理信号以及如何实现signal模块。Python解释器处理信号机制需要做好两件事情:

  1. Python解释器与操作系统有关信号的交互

  2. Python解释器实现信号语义的API接口和模块

img

大体上,Python解释器对信号的实现总体思路比较简单。Python解释器对信号做一层封装,在这层封装中处理信号,以及信号发生时的回调函数,使之能够纳入整个Python虚拟机的运行中。我们先从信号的初始化开始一点点揭露整个运作机制。

信号机制的初始化

信号机制的初始化是在Python初始化整个解释器时开始的,Python在初始化函数中调用initsigs来进行整个系统以及singal模块的初始化。

直接进入到singalmodule.c中看signal模块以及信号的初始化 。

可以看到Python将用户自定义信号处理函数保存在Handler数组中,而实际上向系统注册signal_handler函数。这个signal_handler函数成为信号发生时沟通Python解释器和用户自定义信号处理函数的桥梁。可以从signal.signal的实现中清楚的看到这一点。

信号产生时Python的动作

当信号产生时,操作系统会调用Python解释器注册的信号处理函数,即上文中的signal_handler函数。这个函数将对应的Handler结构中的信号产生标志tripped设置为1,然后将一个统一信号处理函数trip_signal作为pending_call注册到Python虚拟机的执行栈中。于是,Python在虚拟机执行过程中调用pending_call并执行各个用户自定义的信号处理函数。

这里面的PyErr_CheckSignals函数也会被其他模块调用直接信号的处理。例如,在file.read读取文件过程中中断,Python对调用该函数进行信号处理。至此,可以看到整个信号处理的流程:

  1. 初始化signal模块,将对应的操作系统信号值、函数转化成Python对象

  2. 用户设置信号就向操作系统注册函数signal_handler,并将用户自定义信号处理函数设置到对应的Handler数组中

  3. 当信号发生时,操作系统调用signal_handler设置tripped=1,然后调用trip_signal将统一处理函数checksignals_witharg作为pendingcall注册到Python虚拟机的执行栈中。

  4. Python虚拟机在处理pendingcall时调用checksignals_withargs,从而信号处理函数得以执行。

  5. 另外,Python其他模块可以直接调用PyErr_CheckSignals进行信号处理。

Python信号的语义

通过注释以及代码剖析可以归纳Python的信号语义:

  • 只有主线程能够设置、捕获和处理信号

  • 信号设置一直有效(signal_handler中会再次注册信号处理函数)

  • 多次信号,可能会被合并处理一次

  • 按照信号值从小到大处理

信号实例

主线程才能捕获信号

  • [1] Python中的线程都是分离的,因此主线程很快退出。信号不能发送到主线程,因此不能被执行。

信号可能只被处理一次

  • [1] 主线程的t.join一直阻塞,因此在子线程没有退出前不能处理信号。(C语言的信号处理是可以打断堵塞信号的)

  • [2] 信号在有机会处理之前发生了两次信号,但是只处理了一次。

Python信号的特殊性

Python的信号语义与Linux的C语言的信号语义有一些不同。

  • Python信号的处理函数会一直有效;而Linux除非特殊设置否则信号处理函数默认只调用一次就被恢复

  • Python信号只能在主线程中设置、捕获和处理

  • Python信号不能打断堵塞操作(因为信号发生时子线程在运行)

最后更新于

这有帮助吗?