4 Signals
Published:
Signals and system calls
Sending signals
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo); // same as kill(), but yourself
- Both return: 0 if OK, -1 on error
- if
pid < 0
, the signal is sent to the process group withpgid == |pid|
Terminal-generated signals:
Ctrl-C
sendsSIGINT
to foreground process groupCtrl-\
sendsSIGQUIT
to foreground PGCtrl-Z
sendsSIGTSTP
to foreground PG
signal()
typedef void (*sighandler_t)(int); // type: void (*)(int)
sighandler_t signal(int signum, sighandler_t handler);
Takes a number, and a pointer to the handler. Returns the old handler. handler
can be:
SIG_IGN
: ignore the signalSIG_DFL
: take the default action- a handler (function) of type
void (*)(int)
called to handle signal
Unreliability
read()
Slow system call
static void sig_int(int); /* our signal-catching function */
int main(void)
{
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal error");
}
void sig_int(int signo)
printf("interrupt\n%% ");
With this, the program prints “interrupt”, but still terminated…
Signal is a very old, and not portable. When signal is caught, the handler
- “Slow” system calls (can stuck forever (
read()
)) may get interrupted on signals- Slow underlying
read()
syscall gets interrupted.errno
set toEINTR
, causesfgets()
to returnNULL
.errno
is a global variable set to the ID of the last error- some OS kernels (e.g., FreeBSD, macOS) automatically restart the interrupted syscall
- Fix: check
EINTR
and manually restart the syscall- ideally, specify restart semantics in handler installation instead of doing it manually
- Slow underlying
- Signals get lost (de-registered once triggered)
- Handler set with
signal()
may reset after each signal - Fix: Re-register the hander after detecting
EINTR
- but race condition: what if we get another signal before we set disposition?
- ideally, specify disposition reset semantics in handler installation instead of doing it manually
- Also not portable across OSs
- Handler set with
[[#Reentrancy]] issue:
- can’t call
malloc()
,free()
, functions with static buffers, standard I/O functions are unsafe! - It will enter functions asynchronously, might corrupt data
alarm()
and pause()
alarm()
for timeout, generates SIGALRM
. Default behavior is to terminate
static void sig_alrm(int);
int main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
alarm(10); // sets alarm
if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
err_sys("read error");
// if something typed without 10 seconds, alarm will go
alarm(0); // cancels alarm
write(STDOUT_FILENO, line, n);
exit(0);
}
static void sig_alrm(int signo)
{
/* nothing to do, just return to interrupt the read */
}
Problems:
- Does not work if slow syscall
read()
gets automatically restarted - If system too busy and this program gets hanged by the OS. Alarm taken by OS
- Solution:
select()
- Solution:
Portable solution
- Generated when the event triggering the signal occurs
- Signal pending…
- Delivered when the signal action taken
- A process can block the signal delivery, so it stays at pending until unblocked
Signal sets
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
// All four above return: 0 if OK, -1 on error
int sigismember(const sigset_t *set, int signo);
// Returns: 1 if true, 0 if false, -1 on error
Implementation: bit mask
sigaction()
int sigaction(int signo, struct sigaction *act, struct sigaction *oact);
// Returns: 0 if OK, -1 on error
struct sigaction {
void (*sa_handler)(int); /* addr of signal handler, */
/* or SIG_IGN, or SIG_DFL */
sigset_t sa_mask; /* additional sigs to block while handling current sig*/
int sa_flags; /* signal options, Figure 10.16 */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
sigset_t sa_mask
: additional signal set to temporarily block when signo
is handled by sa_handler
signo
itself is automatically blocked while insa_handler
int sa_flags
: handling options – some notable ones:SA_INTERRUPT
: Don’t automatically restart slow system call (default)SA_RESTART
: Automatically restart slow system call (opposite)SA_RESETHAND
: Resetsigno
toSIG_DFL
(sig_default, auto de-register)
Reliable implementation of signal()
with sigaction()
Sigfunc *signal(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func; // set handler to signal that comes in
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
// calls sigaction()
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
Signal mask
A set of signals currently [[#^258d08 |blocked]] from delivery
- Can be examined or changed
int sigprocmask(int how, sigset_t *set, sigset_t *oset);
Manipulates process’s signal mask
oset
: old signal mask setset
: New sethow
: how the current signal mask is modified (block, unblock, change)
int sigpending(sigset_t *set);
Retrieves a set of pending, blocked signals to *set
Both return 0 if OK, -1 on error