What is the difference between sigaction and signal?

前端 未结 9 880
离开以前
离开以前 2020-11-22 06:23

I was about to add an extra signal handler to an app we have here and I noticed that the author had used sigaction() to set up the other signal handlers. I was

9条回答
  •  猫巷女王i
    2020-11-22 06:43

    In short:

    sigaction() is good and well-defined, but is a Linux function and so it works only on Linux. signal() is bad and poorly-defined, but is a C standard function and so it works on anything.

    What do the Linux man pages have to say about it?

    man 2 signal (see it online here) states:

    The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.

    Portability The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.

    In other words: don't use signal(). Use sigaction() instead!

    What does GCC think?

    Compatibility Note: As said above for signal, this function should be avoided when possible. sigaction is the preferred method.

    Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

    So, if both Linux and GCC say not to use signal(), but to use sigaction() instead, that begs the question: how the heck do we use this confusing sigaction() thing!?

    Usage Examples:

    Read GCC's EXCELLENT signal() example here: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

    And their EXCELLENT sigaction() example here: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

    After reading those pages, I came up with the following technique for sigaction():

    1. sigaction(), since it's the right way to attach a signal handler, as described above:

    #include   // errno
    #include  // sigaction()
    #include   // printf()
    #include  // strerror()
    
    #define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
    #define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "
    
    /// @brief      Callback function to handle termination signals, such as Ctrl + C
    /// @param[in]  signal  Signal number of the signal being handled by this callback function
    /// @return     None
    static void termination_handler(const int signal)
    {
        switch (signal)
        {
        case SIGINT:
            printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
            break;
        case SIGTERM:
            printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal);
            break;
        case SIGHUP:
            printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
            break;
        default:
            printf("\nUnk signal (%i) caught.\n", signal);
            break;
        }
    
        // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.
    
    
        exit(signal);
    }
    
    /// @brief      Set a new signal handler action for a given signal
    /// @details    Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`),
    ///             which means they are currently intentionally ignored. GCC recommends this "because non-job-control
    ///             shells often ignore certain signals when starting children, and it is important for children
    ///             to respect this." See
    ///             https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    ///             and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
    ///             Note that termination signals can be found here:
    ///             https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
    /// @param[in]  signal  Signal to set to this action
    /// @param[in]  action  Pointer to sigaction struct, including the callback function inside it, to attach to this signal
    /// @return     None
    static inline void set_sigaction(int signal, const struct sigaction *action)
    {
        struct sigaction old_action;
    
        // check current signal handler action to see if it's set to SIGNAL IGNORE
        sigaction(signal, NULL, &old_action);
        if (old_action.sa_handler != SIG_IGN)
        {
            // set new signal handler action to what we want
            int ret_code = sigaction(signal, action, NULL);
            if (ret_code == -1)
            {
                printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n"
                       "  errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno));
            }
        }
    }
    
    int main(int argc, char *argv[])
    {
        //...
    
        // Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function
        // `signal()`: "It is better to use sigaction if it is available since the results are much more reliable."
        // Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
        // and https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
        // See here for official gcc `sigaction()` demo, which this code is modeled after:
        // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
    
        // Set up the structure to specify the new action, per GCC's demo.
        struct sigaction new_action;
        new_action.sa_handler = termination_handler; // set callback function
        sigemptyset(&new_action.sa_mask);
        new_action.sa_flags = 0;
    
        // SIGINT: ie: Ctrl + C kill signal
        set_sigaction(SIGINT, &new_action);
        // SIGTERM: termination signal--the default generated by `kill` and `killall`
        set_sigaction(SIGTERM, &new_action);
        // SIGHUP: "hang-up" signal due to lost connection
        set_sigaction(SIGHUP, &new_action);
    
        //...
    }
    

    2. And for signal(), even though its not a good way to attach a signal handler, as described above, it's still good to know how to use it.

    Here's the GCC demonstration code copy-pasted, as it's about as good as it's going to get:

    #include 
    
    void
    termination_handler (int signum)
    {
      struct temp_file *p;
    
      for (p = temp_file_list; p; p = p->next)
        unlink (p->name);
    }
    
    int
    main (void)
    {
      …
      if (signal (SIGINT, termination_handler) == SIG_IGN)
        signal (SIGINT, SIG_IGN);
      if (signal (SIGHUP, termination_handler) == SIG_IGN)
        signal (SIGHUP, SIG_IGN);
      if (signal (SIGTERM, termination_handler) == SIG_IGN)
        signal (SIGTERM, SIG_IGN);
      …
    }
    

    The main links to be aware of:

    1. Standard Signals: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
      1. Termination Signals: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
    2. Basic Signal Handling, including official GCC signal() usage example: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    3. Official GCC sigaction() usage example: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
    4. Signal sets, including sigemptyset() and sigfillset(); I still don't understand these exactly, but know they are important: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

    See also:

    1. TutorialsPoint C++ Signal Handling [with excellent demo code]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
    2. https://www.tutorialspoint.com/c_standard_library/signal_h.htm

提交回复
热议问题