Generally, a program does not block signals indefinitely - it might as well ignore them by setting their actions to SIG_IGN. But it is useful to block signals briefly, to prevent them from interrupting sensitive operations.
Is Blocking a signal similar to Ignoring a signal ?
No, blocking a signal is different from ignoring a signal. When a process blocks a signal, the operating system does not deliver the signal until the process unblocks the signal. A process blocks a signal by modifying its signal mask with sigprocmask. But when a process ignores a signal, the signal is delivered and the process handles it by throwing it away.
How Blocking Signals is Useful ?
Temporary blocking of signals with sigprocmask gives you a way to prevent interrupts during critical parts of your code. If signals arrive in that part of the program, they are delivered later, after you unblock them.
One example where this is useful is for sharing data between a signal handler and the rest of the program. If the type of the data is not sig_atomic_t, then the signal handler could run when the rest of the program has only half finished reading or writing the data. This would lead to confusing consequences.
To make the program reliable, you can prevent the signal handler from running while the rest of the program is examining or modifying that data - by blocking the appropriate signal around the parts of the program that touch the data. Blocking signals is also necessary when you want to perform a certain action only if a signal has not arrived.
All signal blocking functions use a data structure called a signal set to specify what signals are affected. Thus, every activity involves two stages: creating the signal set, and then passing it as an argument to a library function. These facilities are declared in the header file signal.h.
The sigset_t data type is used to represent a signal set. Internally, it may be implemented as either an integer or structure type. For portability, use only the functions described below to initialize, change, and retrieve information from sigset_t objects - don't try to manipulate them directly.
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
sigemptyset function initializes the signal set given by set to empty, with all signals excluded from the set.
sigfillset function initializes set to full, including all signals.
sigaddset and sigdelset functions add and delete respectively signal signum from set.
sigismember function tests whether signum is a member of set.
Objects of type sigset_t must be initialized by a call to either sigemptyset or sigfillset before being passed to the functions sigaddset, sigdelset and sigismember.
For more information checkout: man 3 sigsetops
The collection of signals that are currently blocked is called the signal mask. Each process has its own signal mask. When you create a new process, it inherits its parent's mask. You can block or unblock signals with total flexibility by modifying the signal mask.
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
In a traditional single-threaded application, sigprocmask system call can be used to fetch and/or manipulate the signal mask of the calling thread.
how determines what operation to be performed on the signal mask.
If oldset is non-null, the previous value of the signal mask is stored in oldset.
set determines list of signals to be set in blocking state.
Signals, such as SIGSTOP and SIGKILL, cannot be blocked. If an attempt is made to block these signals, the system ignores the request without reporting an error.
NOTE: Do not use sigprocmask in multi-threaded processes, because each thread has its own signal mask and there is no single process signal mask. According to POSIX, the behavior of sigprocmask in a multi-threaded process is "unspecified". Instead, use pthread_sigmask.
For more information checkout: man 2 sigprocmask
In the below example we try to block and unblock the SIGINT signal continually in a loop. If a user enters Ctrl-C while SIGINT is blocked, then the program terminates only after it is unblocked. If a user types Ctrl-C while SIGINT is unblocked, the program terminates immediately.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int i;
sigset_t intmask;
int repeatfactor;
double y = 0.0;
if ((sigemptyset(&intmask) == -1) || (sigaddset(&intmask, SIGINT) == -1)){
perror("Failed to initialize the signal mask");
return 1;
}
for ( ; ; ) {
printf("Entering BLOCK state\n");
if (sigprocmask(SIG_BLOCK, &intmask, NULL) == -1)
break;
fprintf(stderr, "SIGINT signal blocked\n");
sleep(2);
printf("Leaving Blocking State & Entering UNBLOCK state\n");
if (sigprocmask(SIG_UNBLOCK, &intmask, NULL) == -1)
break;
fprintf(stderr, "SIGINT signal unblocked\n");
sleep(2);
}
perror("Failed to change signal mask");
return 1;
}
Output:
$ ./a.out
Entering BLOCK state
SIGINT signal blocked
Leaving Blocking State & Entering UNBLOCK state
SIGINT signal unblocked
^C
$ ./a.out
Entering BLOCK state
SIGINT signal blocked
^CLeaving Blocking State & Entering UNBLOCK state
$