内容简介:Python处理信号是在大体上,Python解释器不太可能会操作系统发出的信号立即做回调。因为Python的Opcode操作是原子操作,不允许被中断。所以Python解释器对信号做一层封装,并做好标记,待时机得当的时候来检查并触发相关的回调函数。信号机制的初始化是在Python初始化整个解释器时开始的,Python在初始化函数中调用
起步
Python处理信号是在 signal
模块中,这个模块其实是纯 python 代码对 _signal
的封装。要想知道Python解释器本身如何处理信号以及如何实现的,还需要去了解 signalmodule.c
。其中,比较需要了解的是python解释器与操作系统有关信号的交互。
大体上,Python解释器不太可能会操作系统发出的信号立即做回调。因为Python的Opcode操作是原子操作,不允许被中断。所以Python解释器对信号做一层封装,并做好标记,待时机得当的时候来检查并触发相关的回调函数。
信号机制的初始化
信号机制的初始化是在Python初始化整个解释器时开始的,Python在初始化函数中调用 initsigs()
来进行整个系统以及 singal
模块的初始化。
[Python/pylifecycle.c]
_PyInitError
_Py_InitializeMainInterpreter(PyInterpreterState *interp,
const _PyMainInterpreterConfig *config)
{
...
if (interp->config.install_signal_handlers) {
err = initsigs(); /* Signal handling stuff, including initintr() */
if (_Py_INIT_FAILED(err)) {
return err;
}
}
...
}
而在 initsigs(void)
函数中,则是直接对系统调用的封装:
[Python/pylifecycle.c]
static _PyInitError
initsigs(void)
{
#ifdef SIGPIPE
PyOS_setsig(SIGPIPE, SIG_IGN); // 忽略SIGPIPE
#endif
#ifdef SIGXFZ
PyOS_setsig(SIGXFZ, SIG_IGN); // 忽略SIGXFZ
#endif
#ifdef SIGXFSZ
PyOS_setsig(SIGXFSZ, SIG_IGN); // 忽略SIGXFSZ file size exceeded
#endif
PyOS_InitInterrupts(); /* May imply initsignal() */
if (PyErr_Occurred()) {
return _Py_INIT_ERR("can't import signal");
}
return _Py_INIT_OK();
}
暂时不知道忽略了那几个信号的原因。而 PyOS_InitInterrupts(void)
函数中其实就是 import _signal
:
[Modules/signalmodule.c]
void
PyOS_InitInterrupts(void)
{
PyObject *m = PyImport_ImportModule("_signal");
if (m) {
Py_DECREF(m);
}
}
在 _signal
模块的初始化中:
[Modules/signalmodule.c]
PyMODINIT_FUNC
PyInit__signal(void)
{
PyObject *m, *d, *x;
int i;
main_thread = PyThread_get_thread_ident();
main_pid = getpid();
// 创建signal模块
m = PyModule_Create(&signalmodule);
if (m == NULL)
return NULL;
...
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
// 将SIG_DFL、SIGIGN 转化成Python整数对象
x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0)
goto finally;
x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN);
if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0)
goto finally;
x = PyLong_FromLong((long)NSIG);
if (!x || PyDict_SetItemString(d, "NSIG", x) < 0)
goto finally;
Py_DECREF(x);
...
/*
* 获取signal模块中的默认中断处理函数,
* 实际就是 signal_default_int_handler
*/
x = IntHandler = PyDict_GetItemString(d, "default_int_handler");
if (!x)
goto finally;
Py_INCREF(IntHandler);
/*
* 初始化Python解释器中的Handler,
* 这个数组存储每个用户自定义的信号处理函数
* 以及标志是否发生该信号的标志。
*/
_Py_atomic_store_relaxed(&Handlers[0].tripped, 0);
for (i = 1; i < NSIG; i++) {
void (*t)(int);
t = PyOS_getsig(i);
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
if (t == SIG_DFL)
Handlers[i].func = DefaultHandler;
else if (t == SIG_IGN)
Handlers[i].func = IgnoreHandler;
else
Handlers[i].func = Py_None; /* None of our business */
Py_INCREF(Handlers[i].func);
}
//为 SIGINT 设置默认的信号处理函数signal_handler
if (Handlers[SIGINT].func == DefaultHandler) {
/* Install default int handler */
Py_INCREF(IntHandler);
Py_SETREF(Handlers[SIGINT].func, IntHandler);
PyOS_setsig(SIGINT, signal_handler);
}
// 实现signal模块中的各个 SIGXXX 信号值和名称
#ifdef SIGHUP
if (PyModule_AddIntMacro(m, SIGHUP))
goto finally;
#endif
....
if (PyErr_Occurred()) {
Py_DECREF(m);
m = NULL;
}
finally:
return m;
}
可以看到,用户自定义的处理函数将会保存在 Handler
数组中,而实际上向操作系统注册 signal_signal_impl
函数。这个函数将作为Python解释器和用户自定义处理函数的桥梁:
[Modules/signalmodule.c]
static PyObject *
signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
/*[clinic end generated code: output=b44cfda43780f3a1 input=deee84af5fa0432c]*/
{
PyObject *old_handler;
void (*func)(int);
#ifdef MS_WINDOWS
/* Validate that signalnum is one of the allowable signals */
switch (signalnum) {
case SIGABRT: break;
case SIGTERM: break;
...
default:
PyErr_SetString(PyExc_ValueError, "invalid signal value");
return NULL;
}
#endif
// 只有主线程才能设置信号处理函数
if (PyThread_get_thread_ident() != main_thread) {
PyErr_SetString(PyExc_ValueError,
"signal only works in main thread");
return NULL;
}
if (signalnum < 1 || signalnum >= NSIG) {
PyErr_SetString(PyExc_ValueError,
"signal number out of range");
return NULL;
}
if (handler == IgnoreHandler)
func = SIG_IGN;
else if (handler == DefaultHandler)
func = SIG_DFL;
else if (!PyCallable_Check(handler)) {
PyErr_SetString(PyExc_TypeError,
"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object");
return NULL;
}
else
func = signal_handler; // Python解释器向系统注册的都是signal_handler函数
/* Check for pending signals before changing signal handler */
if (PyErr_CheckSignals()) {
return NULL;
}
if (PyOS_setsig(signalnum, func) == SIG_ERR) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
// 把实际的用户自定义信号处理函数,放入对应的Handler数组中进行替换
old_handler = Handlers[signalnum].func;
Py_INCREF(handler);
Handlers[signalnum].func = handler;
if (old_handler != NULL)
return old_handler;
else
Py_RETURN_NONE;
}
函数 signal_handler
是直接由C信号机制进行的回调。如果用户注册了信号处理函数,那么会取代旧的处理函数。
信号产生时
当C层发出信号并进行回调 signal_handler(int sig_num)
:
[Modules/signalmodule.c]
static void
signal_handler(int sig_num)
{
int save_errno = errno;
/* See NOTES section above */
if (getpid() == main_pid)
{
trip_signal(sig_num);
}
#ifndef HAVE_SIGACTION
#ifdef SIGCHLD
/* To avoid infinite recursion, this signal remains
reset until explicit re-instated.
Don't clear the 'func' field as it is our pointer
to the Python handler... */
if (sig_num != SIGCHLD)
#endif
/* If the handler was not set up with sigaction, reinstall it. See
* Python/pylifecycle.c for the implementation of PyOS_setsig which
* makes this true. See also issue8354. */
PyOS_setsig(sig_num, signal_handler);
#endif
/* Issue #10311: asynchronously executing signal handlers should not
mutate errno under the feet of unsuspecting C code. */
errno = save_errno;
#ifdef MS_WINDOWS
if (sig_num == SIGINT)
SetEvent(sigint_event);
#endif
}
static void
trip_signal(int sig_num)
{
unsigned char byte;
int fd;
Py_ssize_t rc;
// 标记位设为1 ,表示信号产生了
_Py_atomic_store_relaxed(&Handlers[sig_num].tripped, 1);
/* Set is_tripped after setting .tripped, as it gets
cleared in PyErr_CheckSignals() before .tripped. */
// 如果正在处理信号,则不再向Python虚拟机提交
_Py_atomic_store(&is_tripped, 1);
/* Notify ceval.c */
_PyEval_SignalReceived();
#ifdef MS_WINDOWS
fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int);
#else
fd = wakeup.fd;
#endif
if (fd != INVALID_FD) {
byte = (unsigned char)sig_num;
#ifdef MS_WINDOWS
...
#endif
{
/* _Py_write_noraise() retries write() if write() is interrupted by
a signal (fails with EINTR). */
rc = _Py_write_noraise(fd, &byte, 1);
if (rc < 0) {
if (wakeup.warn_on_full_buffer ||
(errno != EWOULDBLOCK && errno != EAGAIN))
{
// 向Python虚拟机提交pending_call,纳入到整个虚拟机的执行过程中
Py_AddPendingCall(report_wakeup_write_error,
(void *)(intptr_t)errno);
}
}
}
}
}
当 C语言 触发回调后,该回调函数会进行设置标记位并将 report_wakeup_write_error
加入到虚拟机的执行过程中,通过跟踪会调用 PyErr_CheckSignals()
进行信号的检查:
[Modules/signalmodule.c]
int
PyErr_CheckSignals(void)
{
int i;
PyObject *f;
if (!_Py_atomic_load(&is_tripped))
return 0;
if (PyThread_get_thread_ident() != main_thread)
return 0;
// 在处理信号了,将标志位设为0
_Py_atomic_store(&is_tripped, 0);
if (!(f = (PyObject *)PyEval_GetFrame()))
f = Py_None;
// 按照信号值从小到大依次调用对应的信号处理函数
for (i = 1; i < NSIG; i++) {
if (_Py_atomic_load_relaxed(&Handlers[i].tripped)) {
PyObject *result = NULL;
PyObject *arglist = Py_BuildValue("(iO)", i, f);
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
if (arglist) {
// 调用回调函数
result = PyEval_CallObject(Handlers[i].func,
arglist);
Py_DECREF(arglist);
}
if (!result) {
_Py_atomic_store(&is_tripped, 1);
return -1;
}
Py_DECREF(result);
}
}
return 0;
}
这里面的 PyErr_CheckSignals
函数允许被其他模块调用直接信号的处理。
总结
可以看到整个信号处理的流程:
- 初始化signal模块,将对应的操作系统信号值、函数转化成Python对象
- 用户设置信号就向操作系统注册函数signal_handler,并将用户自定义信号处理函数设置到对应的Handler数组中
- 当信号发生时,操作系统调用signal_handler设置tripped=1,然后调用trip_signal将统一处理函数checksignals_witharg作为pendingcall注册到Python虚拟机的执行栈中。
- Python虚拟机在处理pendingcall时调用checksignals_withargs,从而信号处理函数得以执行。
- 另外,Python其他模块可以直接调用PyErr_CheckSignals进行信号处理。
对于书写python代码的开发这而言:
- 只有主线程能够设置、捕获和处理信号
- 信号设置一直有效(signal_handler中会再次注册信号处理函数)
- 多次信号,可能会被合并处理一次
- 按照信号值从小到大处理
以上所述就是小编给大家介绍的《Python内核阅读(二十五):信号处理机制》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- xenomai内核解析之信号signal(二)---xenomai信号处理机制
- Python信号处理
- Linux信号处理机制
- 写给 PHP 程序员的信号处理教程
- 音频可视化中的信号处理方案
- 开放下载!《阿里语音与信号处理技术》精选集
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
R语言实战(第2版)
[美] Robert I. Kabacoff / 王小宁、刘撷芯、黄俊文等 / 人民邮电出版社 / 2016-5 / 99.00元
本书注重实用性,是一本全面而细致的R指南,高度概括了该软件和它的强大功能,展示了使用的统计示例,且对于难以用传统方法处理的凌乱、不完整和非正态的数据给出了优雅的处理方法。作者不仅仅探讨统计分析,还阐述了大量探索和展示数据的图形功能。新版做了大量更新和修正,新增了近200页内容,介绍数据挖掘、预测性分析和高级编程。一起来看看 《R语言实战(第2版)》 这本书的介绍吧!