Skip to content

Signals Set

  • Many signal-related system calls need to be able to represent a group of different signals.

Initialize a signal set: sigemptyset() and sigfillset()

  • A signal set must be initialize using 1 in these 2 system calls
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
// Both return 0 on success, or –1 on error

Add and remove a signal: sigaddset() and sigdelset()

#include <signal.h>
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
// Both return 0 on success, or –1 on error

Check member: sigismember()

#include <signal.h>
int sigismember(const sigset_t *set, int sig);
// Returns 1 if sig is a member of set, otherwise 0

Other non-standard system calls

#define _GNU_SOURCE
#include <signal.h>
int sigandset(sigset_t *set, sigset_t *left, sigset_t *right);
int sigorset(sigset_t *dest, sigset_t *left, sigset_t *right);
// Both return 0 on success, or –1 on error
int sigisemptyset(const sigset_t *set);
// Returns 1 if sig is empty, otherwise 0

The Signal Mask (blocking Signal delivery)

  • For each process, the kernel maintains a signal mask—a set of signals whose delivery to the process is currently blocked. If a signal that is blocked is sent to a process, delivery of that signal is delayed until it is unblocked by being removed from the process signal mask.

  • A signal may be added to the signal mask in the following ways:

    • sigaction(): adding the signal that caused its invocation or additional set of signals
    • sigprocmask(): can be used at any time to explicitly add signals to, and remove signals from, the signal mask.

sigprocmask()

  • Used to fetch and/or change the signal mask of the calling thread.
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// Returns 0 on success, or –1 on error

Explain:

The how argument determines the changes that sigprocmask() makes to the signal mask:

  • SIG_BLOCK: The set of blocked signals is the union of the current set (oldset) and the set argument.
  • SIG_UNBLOCK: The signals in the signal set pointed to by set are removed from the signal mask. Unblocking a signal that is not currently blocked doesn’t cause an error to be returned
  • SIG_SETMASK: The signal set pointed to by set is assigned to the signal mask (oldset = set)

Note: If we want to retrieve the signal mask without changing it, then we can specify NULL for the set argument, in which case the how argument is ignored.

Pending Signals

If a process receives a signal that it is currently blocking, that signal is added to the process’s set of pending signals. When (and if) the signal is later unblocked, it is then delivered to the process. To determine which signals are pending for a process, we can call sigpending().

#include <signal.h>
int sigpending(sigset_t *set);
// Returns 0 on success, or –1 on error
  • The mask of pending signals is returned in set
  • Note that set of pending signals is only a mask; it indicates whether or not a signal has occurred, but not how many times it has occurred. In other words, if the same signal is generated multiple times while it is blocked, then it is recorded in the set of pending signals, and later delivered, just once

Example

  • Consider this example: sig_sender.c and sig_receiver.c
sig_sender.c
#include "dbg.h"
#include <signal.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
int numSigs, sig, j;
pid_t pid;
check(argc >= 4 && strcmp(argv[1], "--help") != 0,
"%s pid num-sigs sig-num [sig-num-2]\n", argv[0]);
pid = (long)atoi(argv[1]);
numSigs = atoi(argv[2]);
sig = atoi(argv[3]);
log_info("%s: sending signal %d to process %ld %d times.", argv[0], sig,
(long)pid, numSigs);
for (j = 0; j < numSigs; j++) {
if (kill(pid, sig) == -1)
sentinel("kill");
}
/* If a fourth command-line argument was specified, send that signal*/
if (argc > 4)
if (kill(pid, atoi(argv[4])) == -1)
sentinel("kill");
log_info("%s: exiting\n", argv[0]);
exit(EXIT_SUCCESS);
error:
return -1;
}
sig_receiver.c
#include "dbg.h"
#include "signal_functions.h"
#include <bits/types/sigset_t.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static int sigCnt[NSIG]; // Counts deliveries of each signal
static volatile sig_atomic_t gotSigint =
0; // Set nonzero if SIGINT is delivered
//
static void handler(int sig) {
if (sig == SIGINT)
gotSigint = 1;
else
sigCnt[sig]++;
}
int main(int argc, char *argv[]) {
int n, numSecs;
sigset_t pendingMask, blockingMask, emptyMask;
log_info("%s: PID is %ld\n", argv[0], (long)getpid());
/*
Setting signal disposition for all signals
* */
for (n = 1; n < NSIG; n++)
(void)signal(n, handler);
if (argc > 1) {
numSecs = atoi(argv[1]);
/* Blocking all signal */
sigfillset(&blockingMask);
if (sigprocmask(SIG_SETMASK, &blockingMask, NULL) == -1)
sentinel("sigprocmask");
log_info("%s: sleep for %d seconds\n", argv[0], numSecs);
sleep(numSecs);
/* Catch all received signals, because are signals are blocked, they will be in pendingMask */
if (sigpending(&pendingMask) == -1)
sentinel("sigpending");
log_info("%s: pending signals are: \n", argv[0]);
printSigset(stdout, "\t\t", &pendingMask);
sigemptyset(&emptyMask); // Unblock all signals
if (sigprocmask(SIG_SETMASK, &emptyMask, NULL) == -1)
sentinel("sigprocmask");
}
// Loop until SIGINT caught
while (!gotSigint)
continue;
for (n = 1; n < NSIG; n++)
if (sigCnt[n] != 0)
log_info("%s: signal %d caught %d times", argv[0], n, sigCnt[n]);
exit(EXIT_SUCCESS);
error:
return -1;
}
/*
./sig_receiver 10 &
*/
  • First run: run sig_receiver.c with arg 15 so it will sleep 15s (all signals are blocked)
Terminal window
mc@gmktec-server:~/Code/c-programming/system-calls/signals$ ./sig_receiver 15 &
[1] 44591
[INFO] (sig_receiver.c:main:36: errno: Invalid argument) ./sig_receiver: sleep for 15 seconds
[INFO] (sig_receiver.c:main:42: errno: Invalid argument) ./sig_receiver: pending signals are:
2 (Interrupt)
10 (User defined signal 1)
[INFO] (sig_receiver.c:main:55: errno: Invalid argument) ./sig_receiver: signal 10 caught 1 times
mc@gmktec-server:~/Code/c-programming/system-calls/signals$ ./sig_sender 44591 1000000 10 2
[INFO] (sig_sender.c:main:17: errno: None) ./sig_sender: sending signal 10 to process 44591 1000000 times
[INFO] (sig_sender.c:main:29: errno: None) ./sig_sender: exiting
  • Second run: run sig_receiver.c without arg so no signal is blocked.
Terminal window
mc@gmktec-server:~/Code/c-programming/system-calls/signals$ ./sig_receiver 15 &
[INFO] (sig_receiver.c:main:24: errno: None) ./sig_receiver: PID is 44721
mc@gmktec-server:~/Code/c-programming/system-calls/signals$ ./sig_sender 44721 1000000 10 2
[INFO] (sig_sender.c:main:17: errno: None) ./sig_sender: sending signal 10 to process 44721 1000000 time
[INFO] (sig_receiver.c:main:55: errno: Invalid argument) ./sig_receiver: signal 10 caught 333149 times
[INFO] (sig_sender.c:main:29: errno: None) ./sig_sender: exiting
  • From the output above, we can see that even though one million signals were sent, only one was delivered to the receiver. Even if a process doesn’t block signals, it may receive fewer signals than are sent to it. This can happen if the signals are sent so fast that they arrive before the receiving process has a chance to be scheduled for execution by the kernel, with the result that the multiple signals are recorded just once in the process’s pending signal set