Standard Signals

Setting Signal Dispositions: signal()
typedef void (*sighandler_t)(int);sighandler_t signal(int sig, sighandler_t handler);
// pointer to previous signal disposition on success, SIG_ERR on error (does not set errno)-
Explain:
int sig: Signal from the figure 8.26sighandler_t handler: a pointer to a function returning nothing and taking 1 integer argument
-
Instead of specifying the address of a function as the handler argument of signal(), we can specify one of the following values:
SIG_DFLandSIG_IGN:SIG_DFL: Reset the disposition of the signal to its default. This is useful for undoing the effect of an earlier call to signal() that changed the disposition for the signal.SIG_IGN: Ignore the signal. If the signal is generated for this process, the kernel silently discards it. The process never even knows that the signal occurred.
-
Example:
#include <signal.h>#include <stdio.h>#include <unistd.h>
void sigint_handler(int sig) { printf("Caught SIGINT\n"); }// Never use `stdio` functions from within a signal handler in real-world applicationint main() { if (signal(SIGINT, sigint_handler) == SIG_ERR) printf("Signal error\n");
pause();}Sending Signals: kill()
- One process can send a signal to another process using the
kill()system call:
#include <signal.h>
int kill(pid_t pid, int sig);// Returns 0 on success, or –1 on error and errno is set to indicate the error.Explain
-
The
pidargument identifies one or more processes to which the signal specified bysigis to be sent:- If
pid> 0, the signal is sent to the process with the process ID specified bypid. - If
pid== 0: the signal is sent to every process in the sameprocess groupas the calling process, including the calling process itself. - If
pid< -1: the signal is sent to all of the processes in theprocess groupwhose ID equals the absolute value ofpid. - If
pid== -1 (broadcast): the signal is sent to every process for which the calling process has permission to send a signal, except init (process ID 1) and the calling process. If a privileged process makes this call, then all processes on the system will be signaled, except for these last two.
- If
-
A process needs appropriate permissions to be able send a signal to another process. The permission rules are as follows:
- A privileged (
CAP_KILL) process may send a signal to any process. - The init process (process ID 1), which runs with user and group of
root, is a special case. It can be sent only signals for which it has a handler installed. - An unprivileged process can send a signal to another process if
RUIDorEUIDof the sending process matches theRUIDorSSUIDof the receiving process.
- A privileged (
-
Error:
- If no process matches the specified pid,
kill()fails and setserrnotoESRCH(“No such process”). - If a process doesn’t have permissions to send a signal to the requested pid, then
kill()fails, settingerrnotoEPERM. Where pid specifies a set of processes (i.e., pid is negative),kill()succeeds if at least one of them could be signaled.
- If no process matches the specified pid,
Example:
#include "dbg.h"#include <errno.h>#include <signal.h>#include <stdlib.h>#include <string.h>
int main(int argc, char *argv[]) { int s, sig, pid; check(argc == 3 && strcmp(argv[1], "--help") != 0, "%s sig-num pid\n", argv[0]);
// Simplify arg check // Assume that sig is correct. pid = atoi(argv[1]); sig = atoi(argv[2]);
s = kill((pid_t)pid, sig); if (s == -1) { // Error if (errno == EPERM) log_info( "Process exists, but we don't have permission to send it a signal"); else if (errno == ESRCH) log_info("Process doesn't exist"); else sentinel("kill"); } else { // Kill successfully log_info("Process exists and we can send it a signal\n"); }
exit(EXIT_SUCCESS);error: return -1;}Sending Signals to the process itself: raise()
Sometimes, it is useful for a process to send a signal to itself
#include <signal.h>int raise(int sig);
// Returns 0 on success, or nonzero on errorExplain
In a single-threaded program, a call to raise() is equivalent to the following call to kill():
kill(getpid(), sig);On a system that supports threads, raise(sig) is implemented as:
pthread_kill(pthread_self(), sig)Error
raise() returns a nonzero value (not necessarily –1) on error. The only
error that can occur with raise() is EINVAL, because sig was invalid.
Sending Signals to a Process group: killpg()
#include <signal.h>int killpg(pid_t pgrp, int sig);
// Returns 0 on success, or –1 on errorExplain
A call to killpg() is equivalent to the following call to kill():
kill(-pgrp, sig);If pgrp == 0, then the signal is sent to all processes in the same process
group as the caller.
Setting Signal Dispositions: setaction()
- One limitation of
signal()is that it does not provide a straightforward way to query or retrieve the current disposition of a signal without altering it. - The
sigaction()system call is an alternative to signal() for setting the disposition of a signal. It provides much more flexibility.
#include <signal.h>int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
// Returns 0 on success, or –1 on errorExplain
-
The
sigargument identifies the signal whose disposition we want to retrieve or change. This argument can be any signal exceptSIGKILLorSIGSTOP -
The
actargument is a pointer to a structure specifying a new disposition for the signal. If we are interested only in finding the existing disposition of the signal, then we can specify NULL for this argument -
The
oldactargument is a pointer to a structure of the same type, and is used to return information about the signal’s previous disposition. If we are not interested in this information, then we can specifyNULLfor this argument. -
The structures pointed to by act and oldact are of the following type:
struct sigaction {void (*sa_handler)(int); /* Address of handler */sigset_t sa_mask; /* Signals blocked during handler invocation */int sa_flags; /* Flags controlling handler invocation */void (*sa_restorer)(void); /* Not for application use */};- The
sa_handleris similar tohandlerargument given tosignal(). - The
sa_mask(interpreted <=>sa_handler!=SIG_IGNorSIG_DFL): specifies a mask of signals which should be blocked during the execution of the signal handler. In addition, the signal which triggered the handler will be blocked, unless theSA_NODEFERflag is used. These signals remain in the process signal mask until the signal handler returns, at which time they are automatically removed. This field allows us to specify a set of signals that aren’t permitted to interrupt execution of this handler. - The
sa_flags: is a bit mask specifying various options controlling how the signal is handled. The following bits may be ORed(|)together in this field:Name Description SA_NOCLDSTOPIf sig is SIGCHLD, don’t generate this signal when a child process is stopped or resumed as a consequence of receiving a signalSA_NOCLDWAIT(since Linux 2.6) If sig is SIGCHLD, don’t transform children into zombies when they terminateSA_NODEFERWhen this signal is caught, don’t automatically add it to the process signal mask while the handler is executing. SA_ONSTACKInvoke the handler for this signal using an alternate stack installed by sigaltstack().SA_RESETHANDWhen this signal is caught, reset its disposition to the default (i.e., SIG_DFL) before invoking the handler. (By default, a signal handler remains established until it is explicitly disestablished by a further call tosigaction().)SA_RESTARTAutomatically restart system calls interrupted by this signal handler SA_SIGINFOInvoke the signal handler with additional arguments providing further information about the signal
- The
Example
#include "dbg.h"#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>
volatile sig_atomic_t unprocessedSig = 0;void sighandler(int sig) { if (sig == SIGINT) unprocessedSig = 1;}int main() { printf("This is pid %d\n", getpid());
struct sigaction act; act.sa_handler = &sighandler;
// All signal will be blocked while our signal handler function is running. // The original signal mask will be restored when our signal handler exits normally; sigfillset(&act.sa_mask);
/* If the signal handler gets run in the middle of system call (like open(), read(),...) and the signal handler returns normally, restart system calls interrupted by the signal handler */ act.sa_flags = SA_RESTART; if (sigaction(SIGINT, &act, NULL) == -1) sentinel("sigaction");
printf("IMPORTANT: kill this process by running 'killall -9 sigaction_demo' " "in another terminal\n"); while (1) { if (unprocessedSig) { unprocessedSig = 0; printf("SIGINT signal occurred.\n"); } } return 0;error: return -1;}