如何在Python中捕获阻塞boost c++方法中的中断信号?
How do I catch an Interrupt signal in Python when inside a blocking boost c++ method?
我有一个用c++编写的工具集,并为Python提供Boost绑定。
最初,这些代码都是c++,我捕获了一个CTRL+C中断:
signal( SIGINT, signalCallbackHandler );
和
void signalCallbackHandler(int /*signum*/)
{
g_myObject->stop();
}
运行正常
然而,现在我已经添加了Python绑定,我正在使用Python来初始化对象。
我最初的想法是这样做:
import signal
def handle_interrupt( signum, frame ) :
g_myObject.stop()
signal.signal( signal.SIGINT, handle_interrupt )
g_myObject = MyObject()
g_myObject.start()
但是,这个信号处理器永远不会被调用。
我应该如何处理这样的中断?我需要在c++中做吗,然后从那里调用Python函数?
你的python信号处理程序没有被调用,因为python将信号处理程序的执行延迟到下一个字节码指令执行之后-参见库文档中的信号,第18.8.1.1节:
Python信号处理程序不会在低级(C)信号处理程序中执行。相反,低级信号处理程序设置一个标志,该标志告诉虚拟机在稍后的点(例如在下一个字节码指令时)执行相应的Python信号处理程序。这会产生如下后果:
- 在C代码中捕获由无效操作引起的同步错误(如
SIGFPE
或SIGSEGV
)几乎没有意义。Python将从信号处理程序返回到C代码,这可能会再次引发相同的信号,导致Python显然挂起。从Python 3.3开始,你可以使用faulthandler
模块来报告同步错误。- 一个完全用C实现的长时间运行的计算(比如在大量文本上的正则表达式匹配)可能会在任意时间内不间断地运行,而不管接收到任何信号。Python信号处理程序将在计算完成时被调用。
这样做的原因是信号可以在任何时候到达,可能在python指令执行的中途。VM开始执行信号处理程序是不安全的,因为VM处于未知状态。因此,python安装的实际信号处理程序只是设置一个标志,告诉VM在当前指令完成后调用信号处理程序。
如果信号在c++函数执行期间到达,那么信号处理程序设置标志并返回到你的c++函数。
如果信号处理程序的主要目的是允许c++函数被中断,那么我建议你放弃Python信号处理程序,并安装一个c++信号处理程序,该信号处理程序设置一个标志,在你的c++代码中触发提前退出(可能会返回一个表明它被中断的值)。
这种方法将允许您使用相同的代码,无论您是从python, c++还是其他绑定调用代码。
我有一个解决方案可以解决这个问题,尽管如果我可以用Python而不是c++捕获信号会更干净。
我之前没有提到的一件事是,MyObject是一个单例,所以我得到它与MyObject.getObject()
def signalHandler( signum ) :
if signum == signal.SIGINT :
MyObject.getObject().stop()
def main() :
signal.signal( signal.SIGINT, handle_interrupt )
myObject = MyObject.getObject()
myObject.addSignalHandler( signal.SIGINT, signalHandler )
myObject.start()
在我的c++代码中,在一个不应该了解Python的地方,我有:
class MyObject
{
public :
void addSignalHandler( int signum, void (*handler)( int, void* ), void *data = nullptr );
void callSignalHandler( int signum );
private :
std::map<int, std::pair<void (*)( int, void* ), void*> > m_signalHandlers;
}
void signalCallbackHandler( int signum )
{
MyObject::getObject()->callSignalHandler( signum );
}
void MyObject::addSignalHandler( int signum, void (*handler)( int, void* ), void *data )
{
m_signalHandlers.insert( std::pair<int, std::pair<void (*)( int, void* ), void *> >( signum, std::make_pair( handler, data ) ) );
signal( signum, signalCallbackHandler );
}
void MyObject::callSignalHandler( int signum )
{
std::map<int, std::pair<void (*)( int, void* ), void*> >::iterator handler = m_signalHandlers.find( signum );
if( handler != m_signalHandlers.end() )
{
handler->second.first( signum, handler->second.second );
}
}
然后在Python绑定中:
void signalHandlerWrapper( int signum, void *data )
{
if( nullptr == data )
{
return;
}
PyObject *func = (PyObject*)( data );
if( PyFunction_Check( func ) )
{
PyObject_CallFunction( func, "i", signum );
}
}
void addSignalHandlerWrapper( MyObject *o, int signum, PyObject *func )
{
Py_INCREF( func );
if( PyFunction_Check( func ) )
{
o->addSignalHandler( signum, &signalHandlerWrapper, func );
}
}
我没有的,我应该添加的是addSignalHandlerWrapper()中的一些东西,它将检查是否已经存在该信号号,如果是,获取它并在添加新信号之前减少引用。我还没有这样做,因为这个功能只用于结束程序,但为了完整性,应该把它放在适当的位置。
无论如何,正如我一开始所说的,这比它可能涉及的更多。它也只适用于我有一个可以跟踪函数指针的单例。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 没有为自己的结构调用列表推回方法
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 在类定义之后定义一个私有方法
- 枚举环境变量的惯用C++14/C++17方法
- 初始化具有非默认构造函数的std::数组项的更好方法