我有一个用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函数?
答案 0 :(得分:1)
您的python信号处理程序未被调用,因为python推迟执行信号处理程序,直到执行下一个字节码指令之后 - 请参阅the library documentation for signal, section 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 ++还是其他绑定调用代码,该方法都允许您使用相同的代码。
答案 1 :(得分:0)
我有 解决方案适用于此,但如果我能用Python而不是C ++捕获信号,它会更清晰。
我之前没有提到的一件事是MyObject是一个单身人士,所以我用fetch
在Python中,我得到了:
MyObject.getObject()
在我的C ++代码中,在一个对Python不了解的领域,我有:
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()
然后在我的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 );
}
}
我没有,我应该添加,是addSignalHandlerWrapper()中的东西,它将检查是否已经存在该信号编号,如果是,则在添加新参考之前获取并减少参考。我还没有这样做,因为这个功能仅用于结束程序,但为了完整性,它应该放在适当的位置。
无论如何,正如我在开始时所说的那样,这种情况比实际情况更为复杂。它也只能用于跟踪函数指针的单例。