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 errorint 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 signalssigprocmask()
: 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 theset
argument.SIG_UNBLOCK
: The signals in the signal set pointed to byset
are removed from the signal mask. Unblocking a signal that is not currently blocked doesn’t cause an error to be returnedSIG_SETMASK
: The signal set pointed to byset
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
andsig_receiver.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;}
#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 signalstatic 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)
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.
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