在阻塞boost c ++方法中,如何在Python中捕获中断信号?

时间:2016-10-08 09:20:30

标签: python c++ boost

我有一个用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函数?

2 个答案:

答案 0 :(得分:1)

您的python信号处理程序未被调用,因为python推迟执行信号处理程序,直到执行下一个字节码指令之后 - 请参阅the library documentation for signal, section 18.8.1.1

  

Python信号处理程序不会在低级(C)信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉虚拟机稍后执行相应的Python信号处理程序(例如在下一个字节码指令处)。这有后果:

     
      
  • 捕获由C代码中的无效操作引起的SIGFPESIGSEGV等同步错误是没有意义的。 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()中的东西,它将检查是否已经存在该信号编号,如果是,则在添加新参考之前获取并减少参考。我还没有这样做,因为这个功能仅用于结束程序,但为了完整性,它应该放在适当的位置。

无论如何,正如我在开始时所说的那样,这种情况比实际情况更为复杂。它也只能用于跟踪函数指针的单例。