Skip to content

Signals, Part 4: Sigaction

Ebrahim Byagowi edited this page May 28, 2018 · 14 revisions

How and why do I use sigaction ?

You should use sigaction instead of signal because it has better defined semantics. signal on different operating system does different things which is bad. sigaction is more portable and is better defined for threads if need be.

To change the "signal disposition" of a process - i.e. what happens when a signal is delivered to your process - use sigaction

You can use system call sigaction to set the current handler for a signal or read the current signal handler for a particular signal.

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

The sigaction struct includes two callback functions (we will only look at the 'handler' version), a signal mask and a flags field -

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
}; 

How do I convert a signal call into the equivalent sigaction call?

Suppose you installed a signal handler for the alarm signal,

signal(SIGALRM, myhandler);

The equivalent sigaction code is:

struct sigaction sa; 
sa.sa_handler = myhandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; 
sigaction(SIGALRM, &sa, NULL)

However, we typically may also set the mask and the flags field. The mask is a temporary signal mask used during the signal handler execution. The SA_RESTART flag will automatically restart some (but not all) system calls that otherwise would have returned early (with EINTR error). The latter means we can simplify the rest of code somewhat because a restart loop may no longer be required.

sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* Restart functions if  interrupted by handler */     

How do I use sigwait?

Sigwait can be used to read one pending signal at a time. sigwait is used to synchronously wait for signals, rather than handle them in a callback. A typical use of sigwait in a multi-threaded program is shown below. Notice that the thread signal mask is set first (and will be inherited by new threads). This prevents signals from being delivered so they will remain in a pending state until sigwait is called. Also notice the same set sigset_t variable is used by sigwait - except rather than setting the set of blocked signals it is being used as the set of signals that sigwait can catch and return.

One advantage of writing a custom signal handling thread (such as the example below) rather than a callback function is that you can now use many more C library and system functions that otherwise could not be safely used in a signal handler because they are not async signal-safe.

Based on http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_sigmask.html

static sigset_t   signal_mask;  /* signals to block         */

int main (int argc, char *argv[]) {
    pthread_t sig_thr_id;      /* signal handler thread ID */
    sigemptyset(&signal_mask);
    sigaddset(&signal_mask, SIGINT);
    sigaddset(&signal_mask, SIGTERM);
    pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);

    /* New threads will inherit this thread's mask */
    pthread_create(&sig_thr_id, NULL, signal_thread, NULL);

    /* APPLICATION CODE */
    ...
}

void *signal_thread (void *arg) {
    int       sig_caught;    /* signal caught       */

    /* Use same mask as the set of signals that we'd like to know about! */
    sigwait(&signal_mask, &sig_caught);
    switch (sig_caught) {
    case SIGINT:     /* process SIGINT  */
        ...
        break;
    case SIGTERM:    /* process SIGTERM */
        ...
        break;
    default:         /* should normally not happen */
        fprintf(stderr, "\nUnexpected signal %d\n", sig_caught);
        break;
    }
}
Clone this wiki locally