2016-10-08 14 views
2

我有一個用C++編寫的工具集,並給出了Python的Boost綁定。在阻塞boost C++方法中,如何在Python中捕獲中斷信號?

最初,該代碼爲所有的C++,我抓了CTRL +ç中斷用:

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函數?

+1

如果您的擴展程序代碼在後臺線程中運行,請確保釋放GIL。在主線程中,不要忘記在C代碼中的信號中斷阻塞系統調用之後,定期或在「errno == EINTR」上調用'PyErr_CheckSignals()'(同時保持GIL)。相關:[Cython,Python和KeyboardInterrupt被忽略](http://stackoverflow.com/q/16769870/4279)。這裏是[更多細節(用俄語) - 查看代碼示例](http://ru.stackoverflow.com/a/573288/23044) – jfs

回答

0

我有a解決方案適用於此,但如果我可以用Python而不是C++捕獲信號,它會更乾淨。

一件事之前我沒有提到的是,myObject的是一個單身,所以我用MyObject.getObject()

在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() 

在我的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()中的一些東西,它將檢查是否已經存在某個信號編號,如果是,則在添加新的函數之前獲取它並減少引用一。我還沒有這樣做,因爲這個功能只用於結束程序,但爲了完整性,它應該放在適當的位置。

無論如何,正如我在開始時所說的那樣,這比參與其中的可能性更大。它也只適用於我有一個可以跟蹤函數指針的單例。

1

你的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指令的執行在任何時間到達,潛在一半。虛擬機開始執行信號處理程序並不安全,因爲虛擬機處於未知狀態。因此,python安裝的實際信號處理程序只是設置一個標誌,告訴VM在當前指令完成後調用信號處理程序。

如果信號在您的C++函數執行過程中到達,那麼信號處理程序會設置標誌並返回到您的C++函數。

如果信號處理程序的主要目的是爲了允許C++函數被中斷,那麼我建議你省去Python信號處理程序並安裝一個C++信號處理程序,該程序設置一個觸發C++中提前退出的標誌代碼(可能返回一個表明它被中斷的值)。

該方法允許您使用相同的代碼,無論您是從Python,C++還是其他綁定中調用代碼。