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_DFL
andSIG_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
pid
argument identifies one or more processes to which the signal specified bysig
is 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 group
as the calling process, including the calling process itself. - If
pid
< -1: the signal is sent to all of the processes in theprocess group
whose 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
RUID
orEUID
of the sending process matches theRUID
orSSUID
of the receiving process.
- A privileged (
-
Error:
- If no process matches the specified pid,
kill()
fails and setserrno
toESRCH
(“No such process”). - If a process doesn’t have permissions to send a signal to the requested pid, then
kill()
fails, settingerrno
toEPERM
. 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 error
Explain
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 error
Explain
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 error
Explain
-
The
sig
argument identifies the signal whose disposition we want to retrieve or change. This argument can be any signal exceptSIGKILL
orSIGSTOP
-
The
act
argument 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
oldact
argument 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 specifyNULL
for 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_handler
is similar tohandler
argument given tosignal()
. - The
sa_mask
(interpreted <=>sa_handler
!=SIG_IGN
orSIG_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_NODEFER
flag 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_NOCLDSTOP
If 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_NODEFER
When this signal is caught, don’t automatically add it to the process signal mask while the handler is executing. SA_ONSTACK
Invoke the handler for this signal using an alternate stack installed by sigaltstack()
.SA_RESETHAND
When 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_RESTART
Automatically restart system calls interrupted by this signal handler SA_SIGINFO
Invoke 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;}