为啥我的信号处理程序(引发异常)不会多次触发?

Posted

技术标签:

【中文标题】为啥我的信号处理程序(引发异常)不会多次触发?【英文标题】: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;

【问题讨论】:

【参考方案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。

以上是关于为啥我的信号处理程序(引发异常)不会多次触发?的主要内容,如果未能解决你的问题,请参考以下文章

Halcon软件每次启动报程序异常停止,收到信号11

如果我每次都从不同的线程调用事件,为啥会从同一个线程触发事件处理程序的多次执行?

raise的使用

如何引发浮点异常

Python面向对象之异常处理

为啥 QNAN == QNAN 不会导致引发 FE_INVALID 异常?