What is the difference between sigaction and signal?

66,419

Solution 1

Use sigaction() unless you've got very compelling reasons not to do so.

The signal() interface has antiquity (and hence availability) in its favour, and it is defined in the C standard. Nevertheless, it has a number of undesirable characteristics that sigaction() avoids - unless you use the flags explicitly added to sigaction() to allow it to faithfully simulate the old signal() behaviour.

  1. The signal() function does not (necessarily) block other signals from arriving while the current handler is executing; sigaction() can block other signals until the current handler returns.
  2. The signal() function (usually) resets the signal action back to SIG_DFL (default) for almost all signals. This means that the signal() handler must reinstall itself as its first action. It also opens up a window of vulnerability between the time when the signal is detected and the handler is reinstalled during which if a second instance of the signal arrives, the default behaviour (usually terminate, sometimes with prejudice - aka core dump) occurs.
  3. The exact behaviour of signal() varies between systems — and the standards permit those variations.

These are generally good reasons for using sigaction() instead of signal(). However, the interface of sigaction() is undeniably more fiddly.

Whichever of the two you use, do not be tempted by the alternative signal interfaces such as sighold(), sigignore(), sigpause() and sigrelse(). They are nominally alternatives to sigaction(), but they are only barely standardized and are present in POSIX for backwards compatibility rather than for serious use. Note that the POSIX standard says their behaviour in multi-threaded programs is undefined.

Multi-threaded programs and signals is a whole other complicated story. AFAIK, both signal() and sigaction() are OK in multi-threaded applications.

Cornstalks observes:

The Linux man page for signal() says:

  The effects of signal() in a multi-threaded process are unspecified.

Thus, I think sigaction() is the only that can be used safely in a multi-threaded process.

That's interesting. The Linux manual page is more restrictive than POSIX in this case. POSIX specifies for signal():

If the process is multi-threaded, or if the process is single-threaded and a signal handler is executed other than as the result of:

  • The process calling abort(), raise(), kill(), pthread_kill(), or sigqueue() to generate a signal that is not blocked
  • A pending signal being unblocked and being delivered before the call that unblocked it returns

the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in Signal Concepts.

So POSIX clearly specifies the behaviour of signal() in a multi-threaded application.

Nevertheless, sigaction() is to be preferred in essentially all circumstances — and portable multi-threaded code should use sigaction() unless there's an overwhelming reason why it can't (such as "only use functions defined by Standard C" — and yes, C11 code can be multi-threaded). Which is basically what the opening paragraph of this answer also says.

Solution 2

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.h>  // errno
#include <signal.h> // sigaction()
#include <stdio.h>  // printf()
#include <string.h> // 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 <signal.h>

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

Solution 3

To me, this below line was enough to decide:

The sigaction() function provides a more comprehensive and reliable mechanism for controlling signals; new applications should use sigaction() rather than signal()

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

Whether you're starting from scratch or modifying an old program, sigaction should be the right option.

Solution 4

They're different interfaces for OS's signal facilities. One should prefer using sigaction to signal if possible as the signal() has implementation-defined (often race prone) behavior and behaves differently on Windows, OS X, Linux and other UNIX systems.

See this security note for details.

Solution 5

signal() is standard C, sigaction() is not.

If you're able to use either (that is, you're on a POSIX system), then use sigaction(); it's unspecified whether signal() resets the handler, meaning that to be portable you have to call signal() again inside the handler. What's worse is that there's a race: if you get two signals in quick succession, and the second is delivered before you reinstall the handler, you'll have the default action, which is probably going to be to kill your process. sigaction(), on the other hand, is guaranteed to use “reliable” signal semantics. You need not reinstall the handler, because it will never be reset. With SA_RESTART, you can also get some system calls to automatically restart (so you don't have to manually check for EINTR). sigaction() has more options and is reliable, so its use is encouraged.

Psst... don't tell anyone I told you this, but POSIX currently has a function bsd_signal() which acts like signal() but gives BSD semantics, which means it's reliable. Its main use is for porting old applications that assumed reliable signals, and POSIX does not recommend using it.

Share:
66,419

Related videos on Youtube

Matthew Smith
Author by

Matthew Smith

Embedded C++/C Linux programmer. Currently also working with Python and Ember (Javascript based web application framework). Working at a networking devices company that specialises in out-of-band management and recovery.

Updated on March 23, 2020

Comments

  • Matthew Smith
    Matthew Smith over 3 years

    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 going to use signal(). To follow convention I should use sigaction() but if I was writing from scratch, which should I choose?

  • Matthew Smith
    Matthew Smith about 15 years
    That's not in my man page! All I get is "DESCRIPTION The signal() system call installs a new signal handler for the signal with number signum. " I need to upgrade to the useful man pages package.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten about 15 years
    That's off of the Mac OS X 10.5 pages.
  • bmdhacks
    bmdhacks about 15 years
    Also verified from the source code of glibc. signal() just calls sigaction()
  • bmdhacks
    bmdhacks about 15 years
    I just looked at the glibc source code and signal() just calls into sigaction(). Also see above where the MacOS man page claims the same.
  • Matthew Smith
    Matthew Smith about 15 years
    that is good to know. I've only ever seen signal handlers used to close things neatly before exiting so I wouldn't usually be relying on behaviour to do with reinstalling the handler.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 12 years
    This description of signal is actually of the Unix System V behavior. POSIX allows either this behavior or the much more sane BSD behavior, but since you can't be sure which one you'll get, it's still best to use sigaction.
  • Ben Burns
    Ben Burns over 11 years
    This isn't true on all implementations of signal, however. If you want to mandate "sigaction" behavior, don't rely on this assumption.
  • ChristianCuevas
    ChristianCuevas about 9 years
    unless you use the flags explicitly added to sigaction() to allow it to faithfully simulate the old signal() behaviour. What flags would those be (specifcally)?
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @AlexFritz: Primarily SA_RESETHAND, also SA_NODEFER.
  • Cornstalks
    Cornstalks almost 9 years
    The Linux man page says: The effects of signal() in a multithreaded process are unspecified. Thus, I think sigaction is the only that can be used safely in a multithreaded process.
  • Bulat M.
    Bulat M. about 7 years
    @Jonathan, could you suggest, what should one use to write portable code dealing with signals that should work on non-POSIX systems too?
  • Jonathan Leffler
    Jonathan Leffler about 7 years
    @BulatM. If you can't use sigaction(), then you're essentially obliged to use the Standard C specification for signal(). However, that gives you an extremely impoverished set of options for what you can do. You can: modify (file scope) variables of type volatile sig_atomic_t; call one of the 'quick exit' functions (_Exit(), quick_exit()) or abort(); call signal() with the current signal number as the signal argument; return. And that's it. Anything else is not guaranteed to be portable. That's so stringent that most people ignore those rules — but the resulting code is dodgy.
  • Victor
    Victor almost 5 years
    That is not interesting. If you look for the old POSIX standard of signal(), then it is the same as in the Linux manual page. pubs.opengroup.org/onlinepubs/009604499/functions/signal.htm‌​l
  • Jonathan Leffler
    Jonathan Leffler about 4 years
    POSIX does not have a function bsd_signal() — some POSIX implementations may have the function, but POSIX itself does not contain such a function (see POSIX).
  • Gabriel Staples
    Gabriel Staples almost 4 years
    For completeness, and to help me and others, can you add a brief demo of registering a callback for both signal() and sigaction() to catch a couple basic signals, ex SIGINT and SIGTERM? This would be extremely helpful to see how to use them both, as sigaction() looks very confusing in comparison, whereas signal() is extremely easy to use. Therefore, until reading this answer I always favored signal(), and now that I've read this answer, I'd like to see a solid and correct example of how to use sigaction().
  • Gabriel Staples
    Gabriel Staples almost 4 years
    Excellent sigaction() demo from GCC themselves: gnu.org/software/libc/manual/html_node/…; and excellent signal() demo from GCC themselves: gnu.org/software/libc/manual/html_node/…. Notice that in the signal demo they avoid changing the handler from ignore (SIG_IGN) if that's what it was previously intentionally set to.
  • Gabriel Staples
    Gabriel Staples almost 4 years
    And my answer detailing it and providing extensive code samples: stackoverflow.com/questions/231912/…

Related