【问题标题】:Why doesn't my signal handler (which throws an exception) trigger more than once?为什么我的信号处理程序(引发异常)不会多次触发?
【发布时间】:2011-05-02 18:38:50
【问题描述】:

我正在尝试使用 sigaction 设置异常处理程序。它适用于第一个异常。但是 sigaction 处理程序在第一个异常之后没有被调用,并且当第二个信号发生时程序突然结束。

#include <iostream>
#include <signal.h>
#include <exception>
#include <string.h>

typedef void (*SigactionHandlerPointer)(int iSignal, siginfo_t * psSiginfo, void * psContext);

using namespace std;

void SigactionHookHandler( int iSignal, siginfo_t * psSiginfo, void * psContext )
{
   cout << "Signal Handler Exception Caught: std::exception -- signal : " << iSignal << " from SigactionHookHandler()" << endl;

   throw std::exception();
}

class A
{
public:
   A() {}
   virtual ~A() {}

   virtual void fnct1();
   virtual void fnct2() { fnct3(); }
   virtual void fnct3() { fnct4(); }
   virtual void fnct4();
};

void
A::fnct1()
{
   try {
      fnct2();
   }
   catch( std::exception &ex ) {
      cerr << "Signal Handler Exception Caught" << endl;
   }
   catch (...)
   {
      cerr << "Unknow Exception Caught: " << endl;
   }
}

void
A::fnct4()
{
   *(int *) 0 = 0;  // Access violation
}

int main()
{
   struct sigaction oNewSigAction;
   struct sigaction oOldSigAction;

   memset(&oNewSigAction, 0, sizeof oNewSigAction);

   oNewSigAction.sa_sigaction = SigactionHookHandler;
   oNewSigAction.sa_flags     = SA_SIGINFO;

   int iResult = sigaction( SIGSEGV, &oNewSigAction, &oOldSigAction );

   cout << "sigaction installed handler with status " << iResult << endl;

   A * pA = new A();

   cout << "Next message expected is : <<Signal Handler Exception Caught: std::exception>> to pass this test" << endl;
   pA->fnct1();

   // Second exception will never be call the sigaction handler.
   cout << "Next message expected is : <<Signal Handler Exception Caught: std::exception>> to pass this test" << endl;
   pA->fnct1();

   return 0;
}

【问题讨论】:

    标签: c++ linux exception-handling signals


    【解决方案1】:

    信号和异常彼此不相关。您正在使用的(从异步信号处理程序中抛出异常)只能在支持它的少数编译器之间移植,例如 GCC 和带有-fnon-call-exceptions 的 Intel C/C++。

    也就是说,您忘记做的是解除阻塞信号:当信号处理程序正在执行时,相同信号的传递被阻塞,并且当信号处理程序通过异常退出时它不会被解除阻塞。更改信号处理程序如下:

    void SigactionHookHandler( int iSignal, siginfo_t * psSiginfo, void * psContext
    {
       cout << "Signal Handler Exception Caught: std::exception -- signal : " << iSignal << " from SigactionHookHandler()" << endl;
    
       sigset_t x;
       sigemptyset (&x);
       sigaddset(&x, SIGSEGV);
       sigprocmask(SIG_UNBLOCK, &x, NULL);
    
       throw std::exception();
    }
    

    【讨论】:

    • 我使用 GNU GCC 和 Intel 编译器。我使用了 -fnon-call-exceptions 选项。建议的补丁有效。但是请注意,我更改了 sigprocmask(SIG_UNBLOCK, &x, NULL);到 pthread_sigmask(SIG_UNBLOCK, &x, NULL);因为我有一个多线程应用程序。然而我还是一头雾水。 gcc-lib 手册指出:“......无论如何,当处理程序返回时,系统会恢复进入处理程序之前的掩码......”如果我评论 throw std::exception( ) 在原始代码中,然后在调用信号处理程序后,我确实返回到原始异常
    • (.. 继续上一条评论...) 并且信号处理程序被一次又一次地调用...因此我没想到自己必须解除对信号的阻塞。我错过了什么?
    • @Guy B:因此,当手册说“无论如何”时,它似乎是错误的,尽管有人可能会争辩说抛出异常与“返回”不同。 FWIW,这在 gcc 3.4.6、gcc 4.4.1、gcc 4.5.2 和 gcc 4.6.0 上为我重现。
    • 在思考了这个问题之后,我相信我找到了答案。通过在处理程序中进行抛出,C 运行时库必须将调用者堆栈展开到 catch 进入点。因此,从信号处理程序的返回中恢复原始掩码就丢失了。因此,您必须完成信号处理程序的调用者所做的工作。如果你不掷球没有问题。恢复原来的蒙版。我想知道这是否可以被认为是运行时库的错误?无论如何感谢您的解决方案
    • @Guy B:也许它可以被认为是一个错误——让我们看看 gcc 的人是怎么想的:gcc.gnu.org/bugzilla/show_bug.cgi?id=48854
    【解决方案2】:

    标准 C++ 没有提及信号,或者它们如何与异常交互。您正在尝试执行的操作将完全特定于您使用的操作系统平台,并且可能是您编译代码所使用的特定编译器。

    【讨论】:

    • 更具体地说,我使用的是带有 GNU GCC 4.4.5 编译器的 Linux OS Ubuntu 10.10。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多