Applying C - Signals
Written by Harry Fairhead   
Monday, 09 March 2020
Article Index
Applying C - Signals
Controlling Signals
Sending Signals

Controlling Signals

You can control how a process responds to signals using either the signal function or the sigaction function. Of the two, sigaction is more portable as signal has been implemented in slightly different ways across different versions of Linux, but it is more complicated.

The basic form of a sigaction call is:

sigaction(signum, &action, &oldaction); 

signum is the signal number you want to modify. Both action and oldaction are sigaction structs with action specifying the new setting and oldaction being used to save the existing action state. You can use NULL for either action or old action.

The simplest form of the sigaction struct is:

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

The first field is a pointer to the signal handler function which accepts a single int which is set to the signal number. It can also be set to SIG_DFL to set the default action or SIG_IGN to ignore the signal. The sa_mask field is the bitwise OR of a set of constants which indicate which signals are to be blocked while the handler is running. The sa_flags field controls how the signal is processed; you can mostly leave this cleared apart perhaps for SA_RESTART.

For example, to handle SIG_TERM we first need a function to handle the signal:

void signalHandler(int sig) {
    printf("SIGTERM\n");
}

In principle we shouldn't be using printf within a signal handler for the simple reason that the main program has been interrupted and it could be in the middle of a printf call, and in principle even this printf call could be interrupted and so on. As our main program is going to just use a busy wait we can be reasonably sure that there isn't going to be a problem.

The main program is:

int main(int argc, char** argv) {
    struct sigaction psa={0};
    psa.sa_handler = signalHandler;
    sigaction(SIGTERM, &psa, NULL);
    for (;;) {}
    return (EXIT_SUCCESS);
} 

To make this work we need to include signal.h and in many cases add:

#define _POSIX_C_SOURCE  200809L

as sigaction is a POSIX standard.

The complete program is:

#define _POSIX_C_SOURCE  200809L
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void signalHandler(int sig) {
    printf("SIGTERM\n");
}
int main(int argc, char** argv) {
    struct sigaction psa={0};
    psa.sa_handler = signalHandler;
    sigaction(SIGTERM, &psa, NULL);
    for (;;) {
    }
    return (EXIT_SUCCESS);
} 

When you compile this you will have to run the program from the command line because there is no easy way to send the SIGTERM signal using an IDE such as NetBeans. You can discover the program’s PID using:

ps ax

and then you can send it the signal using:

sudo kill -SIGTERM 32495

using whatever the PID is for the running program. Each time you send the signal you will see the message printed as the default action is being overridden by the handler. To terminate the program use:

sudo kill -SIGKILL 32495

Notice that you don't have to use the other options specified in the struct but they are easy enough to use.

Masking Signals

The mask field is particularly useful because without it your handler is open to being interrupted by another signal. You can block any signal for the duration of the handler, using the appropriate mask, and this can be done using either sigaction or sigprocmask.

The only complication is that the mask is a sigset_t and you shouldn't manipulate its bits directly because the implementation could change. Instead you should use the sigset manipulation functions.

sigemptyset(&sigset);
Clear all bits
sigfillset(&sigset);
Set all bits
sigaddset(&sigset,signum);
Set the bit for signum 
sigdelset(&sigset, signum);
Clear the bit for signum
sigismember(&sigset,
signum);
Returns 1 if signum
is set

As sigaddset and sigdelset change just one bit leaving all the others as they were, you have to initialize the sigset using either sigemptyset or sigfillset. For example, to block all signals while the signal handler is running:

sigset_t mask;
sigfillset(&mask);
struct sigaction psa={0};
psa.sa_handler = signalHandler;
psa.sa_mask=mask;
sigaction(SIGTERM, &psa, NULL);

To block just one signal you would change the initialization of the sigset to:

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask,SIGTERM);

This would block the SIGTERM signal until the handler completed.

You can also use a sigset to block or unblock a signal at any time using the sigprocmask functions:

sigprocmask(how, &set, &oldset);

where how is one of SIG_BLOCK, SIG_UNBLOCK or SIG_SETMASK,

The first two block or unblock the signals in set without changing other bits and the final one sets or unsets them all according to set.

Notice this works with the current process. If you want to modify the signal mask of a thread use pthread_sigmask. Each thread has its own signal mask.

Flags

The flag field can be used to set the behavior of the signal and handler. It currently accepts the bitwise OR of the following flag constants:

SA_NOCLDSTOP

If signum is SIGCHLD, do not receive notification when child processes stop.

SA_NOCLDWAIT

If signum is SIGCHLD, do not transform children into zombies when they terminate.

SA_NODEFER

Do not prevent the signal from being received from within its own signal handler.

SA_ONSTACK

Call the signal handler on an alternate signal stack provided by signaltstack.

SA_RESETHAND

Restore the signal action to the default upon entry to the signal handler.

SA_RESTART

Make certain system calls restartable across signals.

SA_SIGINFO

The signal handler takes three arguments, not one and sa_sigaction should be set instead of sa_handler.

Noteworthy is SA_RESETHAND which makes sigaction behave more like the original signal in that after a single signal has been handled the default action is restored, i.e. the signal handler is just called once.

In chapter but not in this extract

  • Restarting Syscalls
  • Getting More Signal Info
  • Signals and Threads


Last Updated ( Monday, 09 March 2020 )