开发者

Why my linux signal handler run only once

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

void signal_handler(int sig, siginfo_t *siginfo, void* context) 
{ 
  std::cout << " signal_handler " << fetestexcept(FE_ALL_EXCEPT) << std::endl;
  throw "exception"; 
}

void divide() {
  float a = 1000., b = 0.,  c, f = 1e-300;
  c = a / b;
  
  std::cout << c << " and f = &开发者_StackOverflowquot; << f << std::endl;  
}

void init_sig_hanlder() {
  feenableexcept(FE_ALL_EXCEPT);
  
  struct sigaction sa, initial_sa;
  
  sa.sa_sigaction   = &signal_handler ;
  sigemptyset( &sa.sa_mask ) ;
  sa.sa_flags   = SA_SIGINFO;   // man sigaction(3) // allows for void(*)(int,siginfo_t*,void*) handler
  
  sigaction(SIGFPE, &sa, &initial_sa);

}

int main(int argc, char** argv) {
  init_sig_hanlder();
  
  while(true)
    {
      try {
    sleep(1);
    divide();
      }
      catch(const char * a) {
    std::cout << "Exception in catch: " << a << std::endl;
      }    
      catch(...) {
    std::cout << "Exception in ..." << std::endl;
      }    
    }
  
  return 0;
}

Produce the following results on Linux/g++4.2:

signal_handler 0

Exception in catch: exception

inf and f = 0

inf and f = 0

inf and f = 0

inf and f = 0

So, signal handler is executed the first time but the next fp exception does not trigger the handler again. Where am I wrong ?


I don't think throwing an exception inside a signal handler is good practice. The operating system expects the signal handler to return, because the signal is blocked while the handler for it is called. By throwing an exception you prevent the system from unblocking the signal.


As I recall signal handlers have to be declared 'extern "C"' because the library / kernel is going to use C calling conventions, not the C++ calling convention, for your function. But functions that are extern "C" cannot throw exceptions, so at least formally your code is not "right"

Under the hood, I would guess the signal delivery code in Linux does not have a chance to reset or clear the signal masks because you never returned control to the runtime and the kernel.


You should be using sigsetjmp/siglongjmp for code like this, not exceptions.

Using exceptions here is wrong, and the jmp change should be the first step to rule out odd platform specific behaviour (especially since the C++ standard allows for exceptions to be implemented using signal delivery mechanisms).

However, I'm curious if the failure for the SIGFPE to be raised has something to do with the fe*except() state.

Question: What happens to this state before and after doing your initial divide by zero? Perhaps there is an expectation that feclearexcept() is required to get another SIGFPE when you try this again.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜