内容简介: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内核阅读(二十五):信号处理机制》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Security Testing Cookbook
Paco Hope、Ben Walther / O'Reilly Media / 2008-10-24 / USD 39.99
Among the tests you perform on web applications, security testing is perhaps the most important, yet it's often the most neglected. The recipes in the Web Security Testing Cookbook demonstrate how dev......一起来看看 《Web Security Testing Cookbook》 这本书的介绍吧!