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