2009-11-11 57 views
16

我們有一個庫,它處理錯誤報告的許多方面。我一直負責把這個庫移植到Linux上。當運行我的小測試套件時,其中一個測試失敗。下面顯示了一個簡化版本的測試。從信號處理程序中拋出異常

// Compiler: 4.1.1 20070105 RedHat 4.1.1-52 
// Output: Terminate called after throwing an instance of 'int' abort 

#include <iostream> 
#include <csignal> 
using namespace std; 

void catch_signal(int signalNumber) 
{ 
    signal(SIGINT, SIG_DFL); 
    throw(signalNumber); 
} 

int test_signal() 
{ 
    signal(SIGINT, catch_signal); 

    try 
    { 
     raise(SIGINT); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught exception: " << z << endl; 
    } 
    return 0; 
} 

int main() 
{ 
    try 
    { 
     test_signal(); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught unexpected exception: " << z << endl; 
    } 
    return 0; 
} 

我的期望是,捕捉到異常:將顯示消息。實際發生的是程序終止,因爲拋出的int沒有捕獲處理程序。

有幾個關於SO的問題似乎有關。 我找到了一些相關的Google網頁。 '智慧'似乎歸結爲。

  1. 雅不能從信號處理程序拋出異常,導致信號處理器運行自己的堆棧,所以沒有定義處理程序。 Ya可以拋出信號處理程序的異常,只是在棧上重建一個假的 幀,並且你很好。
  2. 雅,我們一直這樣做。它適用於平臺X上的我
  3. 雅,曾經是可用的gcc,但似乎沒有工作任何 更多。嘗試-fnon-call-exceptions選項,也許這將起作用

    代碼在我們的AIX/TRU64/MSVC編譯器/環境中按預期工作。它在我們的Linux環境中失敗。


我找了一些建議,幫助解決這個問題,所以在Linux上的庫行爲將與我的其他平臺,或某種或解決方法可能達到相同的排序功能。
讓程序核心轉儲信號,不是一個可行的選擇。

+0

[本文](http://www.ibm.com/developerworks/library/l-cppexcep/)聲稱它的工作原理;它提到了一些修復可能需要,但它似乎並沒有解釋。 – 2014-10-21 09:15:15

回答

13

信號是完全不同於C++異常。您不能使用C++ try/catch塊來處理信號。具體來說,信號是POSIX的概念,而不是C++語言的概念。內核將信號異步傳送到應用程序,而C++異常是由C++標準定義的同步事件。

在POSIX信號處理程序中,您可以輕鬆地完成的任務非常有限。常用的策略是在信號處理程序中設置一個sig_atomic_t類型的全局標誌,該標誌將被設置爲1,然後可能longjmp被設置爲適當的執行路徑。

請參閱here以幫助編寫適當的信號處理程序。

+8

設置一個標誌是最安全的事情。也就是說,如果你必須使用信號處理程序。信號的傳遞非常昂貴。內核必須構造一個特殊的堆棧框架並將各種機器特定的上下文推送到它上面。您的信號處理程序可以在此框架中安全運行,但不能保證存在進一步的堆棧。這就是爲什麼你不能拋出......編譯器無法爲此上下文生成異常處理代碼的原因。 – 2009-11-11 21:37:07

+3

在某種程度上,安全性不是一個問題。拋出的例外是專門用於以有意義的方式中止程序。無意嘗試重新開始任何操作。 – EvilTeach 2009-11-11 22:02:11

+2

好吧,那麼你可能會用'longjmp'來鎖死處理程序代碼。如果這段代碼通常在'catch'塊中,你可以將它分解爲一個具有'C'鏈接的函數。 – 2009-11-11 22:22:40

4

我會掩蓋每個線程中的所有信號,除了一個會等待sigwait ()的信號。 此線程可以處理無限制的信號,例如拋出異常或使用其他通信機制。

+0

這不是一個多線程程序。 – EvilTeach 2009-11-11 22:00:16

+2

@EvilTeach,但我會同意Bastien Leonard。在sigwait上有一個單獨的線程等待,在處理信號時給你最大的靈活性。否則,你基本上正在使用全局標誌和'longjmp',這不是很漂亮。 – 2009-11-11 22:03:25

+0

@Charles彈出你的longjmp建議作爲回答 – EvilTeach 2009-11-12 16:14:33

4

拋出信號處理程序可能不是一個好主意,因爲堆棧並非必須以與函數調用相同的方式設置,因此從信號處理程序展開可能無法按預期工作。

必須注意C++ ABI使用的任何寄存器,這些寄存器由信號處理機制保存和重新使用。

1

谷歌G ++選項

-fnon-call-exceptions 

這基本上是你想要的。 我認爲這是由於他們的操作系統的壓力從蘋果開發的。 我不確定它在LINUX上的支持程度。 我不確定是否可以捕獲SIGINT - 但是可以捕獲所有CPU觸發信號(例外)。需要此功能(並且不關心意識形態)的編碼人員應該對LINUX開發人員社區施加一定的壓力,以便有朝一日能在LINUX上得到支持 - 這是自從近二十年以來在Windows上得到支持之後。

+1

這是行不通的。 – EvilTeach 2014-11-15 20:56:18

+1

這在Raspberry Pi上不起作用。 **向LINUX開發者社區施加壓力** – 2017-08-06 18:49:15

5

此代碼演示了一種將異常拋出信號處理程序的技術移入代碼中的技術。我感謝Charles的想法。

#include <iostream> 
#include <csignal> 
#include <csetjmp> 

using namespace std; 

jmp_buf gBuffer;  // A buffer to hold info on where to jump to 

void catch_signal(int signalNumber) 
{ 
    //signal(SIGINT, SIG_DFL);   // Switch to default handling 
    signal(SIGINT, catch_signal);  // Reactivate this handler. 

    longjmp    // Jump back into the normal flow of the program 
    (
     gBuffer,  // using this context to say where to jump to 
     signalNumber // and passing back the value of the signal. 
    ); 
} 


int test_signal() 
{ 
    signal(SIGINT, catch_signal); 

    try 
    { 
     int sig; 
     if ((sig = setjmp(gBuffer)) == 0) 
     { 
      cout << "before raise\n"; 
      raise(SIGINT); 
      cout << "after raise\n"; 

     } 
     else 
     { 
      // This path implies that a signal was thrown, and 
      // that the setjmp function returned the signal 
      // which puts use at this point. 

      // Now that we are out of the signal handler it is 
      // normally safe to throw what ever sort of exception we want. 
      throw(sig); 
     } 
    } 
    catch (int &z) 
    { 
     cerr << "Caught exception: " << z << endl; 
    } 

    return 0; 
} 

int main() 
{ 
    try 
    { 
     test_signal(); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught unexpected exception: " << z << endl; 
    } 
    return 0; 
} 
+5

儘管setjmp和longjmp與異常和RAII('ctors/dtors')不兼容。:(你可能會因此泄露資源 – PSkocik 2015-11-13 21:55:07

+0

@PSkocik - ya。我知道,沒關係,我們基本上需要某種記錄的消息,並且應用程序中止。 – EvilTeach 2016-11-23 20:35:54

相關問題