Can exit() fail to terminate process?

前端 未结 3 1296
我寻月下人不归
我寻月下人不归 2021-01-02 02:27

I have a registered a signal handler in my program. Upon receiving an undesired signal (SIGABRT), i call \'exit(-1)\' in signal handler to exit the process. But as noticed o

3条回答
  •  無奈伤痛
    2021-01-02 03:04

    I had analogous problem to the one described by Madar. I needed to perform an action for every signal and quit properly. I wondered through a couple of answers to similar issues and came up with the following explanation/solution.

    Explanation: One issue is that exit() should not be used in signal handlers because it is not one of the async-signal-safe functions (see man signal-safety). This is to say that it may but is not guaranteed to work in signal handlers. As a result you would need to call _exit()/_Exit() (which are async-signal-safe). These however terminate the process instantly, without calling the atexit callbacks and static destructors. My understanding is that for some signals a bit more cleaning can be done than what those functions provide.

    Solution: The solution I came up with is to register your signal handler for all signals and do any extra steps. Then you can reset to the default handler and call raise(signal_number), which is async-signal-safe, to re-send the singal and so execute the default handler.

    Here is a working example that runs default handler only on SIGINT. I think this is too simple to experience the "failing" exit() if you used it in the handler. I tested similar code with an alternative stack to also handle SIGSEGV.

    Note If you want this to work properly in multi-threaded context (e.g. multiple threads causing SIGSEGV at the same time) you need to take some care about synchronization. Threads share the same handler but have separate signal masking.

    #include 
    #include 
    #include 
    
    #include 
    
    #include 
    
    // The actual signal handler
    extern "C" void handleSignal(int sig, siginfo_t *siginfo, void *) {
      // Cannot use printf() - not async-signal-safe 
      // For simplicity I use a single call to write here
      // though it is not guaranteed to write the whole message
      // You need to wrap it in a loop
    
      // Die only on Ctrl+C
      if(sig == SIGINT) {
        const char *msg = "Die\n";
        write(STDERR_FILENO, msg, ::strlen(msg));
        // Reset to use the default handler to do proper clean-up
        // If you want to call the default handler for every singal
        // You can avoid the call below by adding SA_RESETHAND to sa_flags
        signal(sig, SIG_DFL);
        raise(sig);
        return;
      }
    
      // Here we want to handle the signal ourselves
      // We have all the info available
      const char *msg = "Continue\n";
      write(STDERR_FILENO, msg, ::strlen(msg));
    }
    
    int main() {
      // You might want to setup your own alternative stack
      // eg. to handle SIGSEGV correctly
      // sigaltstack() + SA_ONSTACK flag in sa_flag
    
      // Prepare a signal action for handling any signal
      struct sigaction signal_action;
    
      signal_action.sa_sigaction = ::handleSignal;
      signal_action.sa_flags = SA_SIGINFO;
      ::sigfillset(&signal_action.sa_mask);
      // A vector of all signals that lead to process termination by default
      // (see man -s 7 signal)
      const int TERM_SIGNALS[] = {
            SIGHUP,  SIGINT,  SIGQUIT, SIGILL,    SIGABRT, SIGFPE, SIGSEGV,
            SIGPIPE, SIGALRM, SIGTERM, SIGUSR1,   SIGUSR2, SIGBUS, SIGPOLL,
            SIGPROF, SIGSYS,  SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ};
    
      // Register the signal event handler for every terminating signal
      for (auto sig : TERM_SIGNALS) {
        ::sigaction(sig, &signal_action, 0);
      }
    
      while(true);
    
      return 0;
    }
    

提交回复
热议问题