Chapter 10. Signals

Introduction

Signals are software interrupts. They provide a way of handling asynchronous events. Most nontrivial application programs need to deal with signals.

POSIX reliable signals

Signals have been provided since the early versions of the UNIX System, but the signal model provided with systems such as Version 7 was not reliable. Signals could get lost, and it was difficult for a process to turn off selected signals when executing critical regions of code. Both 4.3BSD and SVR3 made changes to the signal model, adding what are called reliable signals. But the changes made by Berkeley and AT&T were incompatible. Fortunately, POSIX.1 standardized the reliable-signal routines, and that is what we describe here.

This chapter starts with an overview of signals and a description of what each signal is normally used for, then discusses problems with earlier implementations, since it is often important to understand what is wrong with an implementation before seeing how to do things correctly. This chapter contains numerous examples that are not entirely correct and a discussion of the defects.

Signal Concepts

Signals are classic examples of asynchronous events. They occur at random times to the process. The process can’t simply test a variable (such as errno) to see whether a signal has occurred; instead, the process has to tell the kernel "if and when this signal occurs, do the following".

Signal dispositions

We can tell the kernel to do one of three things when a signal occurs. This is called the disposition of the signal, or the action associated with a signal. (signal(7))

  1. Ignore the signal. Most signals can be ignored, but two signals can never be ignored: SIGKILL and SIGSTOP.
    • The reason these two signals can’t be ignored is to provide the kernel and the superuser with a surefire way of either killing or stopping any process.
    • If we ignore some of the signals that are generated by a hardware exception (such as illegal memory reference or divide by 0), the behavior of the process is undefined.
  2. Catch the signal. To do this, we tell the kernel to call a function of ours whenever the signal occurs. In our function, we can do whatever we want to handle the condition. For example:
    • If we’re writing a command interpreter, when the user generates the interrupt signal at the keyboard, we probably want to return to the main loop of the program, terminating whatever command we were executing for the user.
    • If the SIGCHLD signal is caught, it means that a child process has terminated, so the signal-catching function can call waitpid to fetch the child’s process ID and termination status.
    • If the process has created temporary files, we may want to write a signal-catching function for the SIGTERM signal (the termination signal that is the default signal sent by the kill command) to clean up the temporary files.
    • Note that the two signals SIGKILL and SIGSTOP can’t be caught.
  3. Let the default action apply. Every signal has a default action. The default action for most signals is to terminate the process.

The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. (signal(7))

UNIX System signals

The following table lists the names of all the signals, an indication of which systems support the signal, and the default action for the signal. The SUS column contains "x" if the signal is defined as part of the base POSIX.1 specification and XSI if it is defined as part of the XSI option. The supported systems are FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8 and Solaris 10.

Name Description ISO C SUS FreeBSD Linux Mac OS X Solaris Default action
SIGABRT abnormal termination (abort) x x x x x x terminate+core
SIGALRM timer expired (alarm) x x x x x terminate
SIGBUS hardware fault x x x x x terminate+core
SIGCANCEL threads library internal use x ignore
SIGCHLD change in status of child x x x x x ignore
SIGCONT continue stopped process x x x x x continue/ignore
SIGEMT hardware fault x x x x terminate+core
SIGFPE arithmetic exception x x x x x x terminate+core
SIGFREEZE checkpoint freeze x ignore
SIGHUP hangup x x x x x terminate
SIGILL illegal instruction x x x x x x terminate+core
SIGINFO status request from keyboard x x ignore
SIGINT terminal interrupt character x x x x x x terminate
SIGIO asynchronous I/O x x x x terminate/ignore
SIGIOT hardware fault x x x x terminate+core
SIGJVM1 Java virtual machine internal use x ignore
SIGJVM2 Java virtual machine internal use x ignore
SIGKILL termination x x x x x terminate
SIGLOST resource lost x terminate
SIGLWP threads library internal use x x ignore
SIGPIPE write to pipe with no readers x x x x x terminate
SIGPOLL pollable event (poll) x x terminate
SIGPROF profiling time alarm (setitimer) x x x x terminate
SIGPWR power fail/restart x x terminate/ignore
SIGQUIT terminal quit character x x x x x terminate+core
SIGSEGV invalid memory reference x x x x x x terminate+core
SIGSTKFLT coprocessor stack fault x terminate
SIGSTOP stop x x x x x stop process
SIGSYS invalid system call XSI x x x x terminate+core
SIGTERM termination x x x x x x terminate
SIGTHAW checkpoint thaw x ignore
SIGTHR threads library internal use x x terminate
SIGTRAP hardware fault XSI x x x x terminate+core
SIGTSTP terminal stop character x x x x x stop process
SIGTTIN background read from control tty x x x x x stop process
SIGTTOU background write to control tty x x x x x stop process
SIGURG urgent condition (sockets) x x x x x ignore
SIGUSR1 user-defined signal x x x x x terminate
SIGUSR2 user-defined signal x x x x x terminate
SIGVTALRM virtual time alarm (setitimer) XSI x x x x terminate
SIGWAITING threads library internal use x ignore
SIGWINCH terminal window size change x x x x ignore
SIGXCPU CPU limit exceeded (setrlimit) XSI x x x x terminate+core/ignore
SIGXFSZ file size limit exceeded (setrlimit) XSI x x x x terminate+core/ignore
SIGXRES resource control exceeded x ignore

The core file

When the default action (in the table above) is labeled "terminate+core", it means that a memory image of the process is left in the file named core of the current working directory of the process. This file can be used with most UNIX System debuggers to examine the state of the process at the time it terminated.

The name of the core file varies among implementations. On Mac OS X 10.6.8, the core file is named core.pid, where pid is the ID of the process that received the signal. On Linux 3.2.0, the name is configured through /proc/sys/kernel/core_pattern. (core(5)) [p315]

Most implementations leave the core file in the current working directory of the corresponding process; Mac OS X places all core files in /cores instead.

The core file will not be generated if:

The permissions of the core file (assuming that the file doesn’t already exist) are usually user-read and user-write, although Mac OS X sets only user-read.

In the table, the signals with a description of "hardware fault" correspond to implementation-defined hardware faults.

Detailed description of signals

signal Function

The simplest interface to the signal features of the UNIX System is the signal function

apue_signal.h

#include <signal.h>

void (*signal(int signo, void (*func)(int)))(int);

/* Returns: previous disposition of signal (see following) if OK, SIG_ERR on error */

Implementations derived from UNIX System V support the signal function, which provides the old unreliable-signal semantics. New applications should not use these unreliable signals. 4.4BSD also provides the signal function, but it is defined in terms of the sigaction function, so using it under 4.4BSD provides the newer reliable-signal semantics. Most current systems follow this strategy except Solaris. [p323]

Because the semantics of signal differ among implementations, we must use the sigaction function instead. We provide an implementation of signal that uses sigaction (later this chapter).

Arguments:

The prototype for the signal function states that the function requires two arguments and returns a pointer to a function that returns nothing (void):

In plain English, this declaration says that the signal handler is passed a single integer argument (the signal number) and that it returns nothing. When we call signal to establish the signal handler, the second argument is a pointer to the function. The return value from signal is the pointer to the previous signal handler.

The signal function prototype can be made much simpler through the use of the following typedef:

typedef void Sigfunc(int);

Then the prototype becomes:

Sigfunc *signal(int, Sigfunc *);

This typedef is included in apue.h and is used with the functions in this chapter.

If we examine the system’s header <signal.h>, we will probably find declarations of the form:

#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1

These constants can be used in place of the "pointer to a function that takes an integer argument and returns nothing", the second argument to signal, and the return value from signal. The three values used for these constants need not be −1, 0, and 1. They must be three values that can never be the address of any declarable function. Most UNIX systems use the values shown. (See Doubts and Solutions for details)

Example:

The following code shows a simple signal handler that catches either of the two user-defined signals and prints the signal number.

#include "apue.h"

static void sig_usr(int);   /* one handler for both signals */

int
main(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
        err_sys("can't catch SIGUSR1");
    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
        err_sys("can't catch SIGUSR2");
    for ( ; ; )
        pause();
}

static void
sig_usr(int signo)      /* argument is signal number */
{
    if (signo == SIGUSR1)
        printf("received SIGUSR1\n");
    else if (signo == SIGUSR2)
        printf("received SIGUSR2\n");
    else
        err_dump("received signal %d\n", signo);
}

We invoke the program in the background and use the kill(1) command to send it signals. The term kill in the UNIX System is a misnomer. The kill(1) command and the kill(2) function just send a signal to a process or process group. Whether that signal terminates the process depends on which signal is sent and whether the process has arranged to catch the signal.

Result:

$ ./a.out &                # start process in background
[1] 7216                   # job-control shell prints job number and process ID
$ kill -USR1 7216          # send it SIGUSR1
received SIGUSR1
$ kill -USR2 7216          # send it SIGUSR2
received SIGUSR2
$ kill 7216                # now send it SIGTERM
[1]+ Terminated ./a.out

When we send the SIGTERM signal, the process is terminated, since it doesn’t catch the signal, and the default action for the signal is termination.

Program Start-Up

When a program is executed, the status of all signals is either default or ignore. All signals are set to their default action, unless the process that calls exec is ignoring the signal. The exec functions change the disposition of any signals being caught to their default action and leave the status of all other signals alone. The reason is that a signal that is being caught by a process that calls exec cannot be caught by the same function in the new program, since the address of the signal-catching function in the caller probably has no meaning in the new program file that is executed. [p325]

With a shell that doesn’t support job control, when we execute a process in the background:

cc main.c &

The shell automatically sets the disposition of the interrupt and quit signals in the background process to be ignored. This is done so that if we type the interrupt character, it doesn’t affect the background process. If this weren’t done and we typed the interrupt character, it would terminate not only the foreground process, but also all the background processes.

Many interactive programs that catch these two signals have code that looks like:

void sig_int(int), sig_quit(int);

if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    signal(SIGINT, sig_int);
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
    signal(SIGQUIT, sig_quit);

Following this approach, the process catches the signal only if the signal is not currently being ignored.

The signal function has a limitation: we are not able to determine the current disposition of a signal without changing the disposition. The sigaction function (discussed later in this chapter) allows us to determine a signal’s disposition without changing it.

Process Creation

When a process calls fork, the child inherits the parent’s signal dispositions. Here, since the child starts off with a copy of the parent’s memory image, the address of a signal-catching function has meaning in the child.

Unreliable Signals

In earlier versions of the UNIX System, signals were unreliable, which means that signals could get lost: a signal could occur and the process would never know about it. [p326]

One problem with these early versions was that the action for a signal was reset to its default each time the signal occurred. The code that was described usually looked like:

    int sig_int(); /* my signal handling function */
    ...
    signal(SIGINT, sig_int); /* establish handler */
    ...

sig_int()
{
    signal(SIGINT, sig_int); /* reestablish handler for next time */
    ... /* process the signal ... */ .
}

The problem with this code fragment is that there is a window of time (after the signal has occurred, but before the call to signal in the signal handler) when the interrupt signal could occur another time. This second signal would cause the default action to occur, which terminates the process. This is one of those conditions that works correctly most of the time, causing us to think that it is correct, when it isn’t.

Another problem with these earlier systems was that the process was unable to turn a signal off when it didn’t want the signal to occur. All the process could do was ignore the signal. There are times when we would like to tell the system "prevent the following signals from interrupting me, but remember if they do occur". The following code catches a signal and sets a flag for the process that indicates that the signal occurred:

int sig_int(); /* my signal handling function */
int sig_int_flag; /* set nonzero when signal occurs */

main()
{
    signal(SIGINT, sig_int); /* establish handler */
    ...
    while (sig_int_flag == 0)
        pause(); /* go to sleep, waiting for signal */
    ...
}

sig_int()
{
    signal(SIGINT, sig_int); /* reestablish handler for next time */
    sig_int_flag = 1; /* set flag for main loop to examine */
}

The process is calling the pause function to put it to sleep until a signal is caught. When the signal is caught, the signal handler just sets the flag sig_int_flag to a nonzero value. The process is automatically awakened by the kernel after the signal handler returns, notices that the flag is nonzero, and does whatever it needs to do. But there is a window of time when things can go wrong. If the signal occurs after the test of sig_int_flag but before the call to pause, the process could go to sleep forever (assuming that the signal is never generated again). This occurrence of the signal is lost.

Interrupted System Calls

In earlier UNIX systems, if a process caught a signal while the process was blocked in a "slow" system call, the system call was interrupted. The system call returned an error and errno was set to EINTR. This was done under the assumption that since a signal occurred and the process caught it, there is a good chance that something has happened that should wake up the blocked system call.

Slow system calls

The system calls are divided into two categories: the "slow" system calls and all the others. The slow system calls are those that can block forever:

The notable exception to these slow system calls is anything related to disk I/O. Although a read or a write of a disk file can block the caller temporarily (while the disk driver queues the request and then the request is executed), unless a hardware error occurs, the I/O operation always returns and unblocks the caller quickly.

Historically, POSIX.1 semantics gave implementations a choice of how to deal with reads and writes that have processed partial amounts of data, implementations derived from System V fail the system call, whereas BSD-derived implementations return partial success. With the 2001 version of the POSIX.1 standard, the BSD-style semantics are required. [p328]

The problem with interrupted system calls is that we now have to handle the error return explicitly. Assuming a read operation and assuming that we want to restart the read even if it’s interrupted, the typical code sequence would be:

again:
    if ((n = read(fd, buf, BUFFSIZE)) < 0) {
        if (errno == EINTR)
            goto again; /* just an interrupted system call */
        /* handle other errors */
    }

Automatic restarts of interrupted system calls

Automatic restarting of certain interrupted system calls were introducted since 4.2BSD to prevent applications from having to handle interrupted system calls. The system calls that were automatically restarted are:

Some applications didn’t want the operation restarted if it was interrupted; 4.3BSD allowed the process to disable this feature on a per-signal basis.

Difference between the signal and sigaction functions on restarts

POSIX.1 requires an implementation to restart system calls only when the SA_RESTART flag is in effect for the interrupting signal. This flag is used with the sigaction function to allow applications to request that interrupted system calls be restarted.

Historically, when using the signal function to establish a signal handler, implementations varied with respect to how interrupted system calls were handled. System V never restarted system calls by default. BSD, in contrast, restarted them if the calls were interrupted by signals. On FreeBSD 8.0, Linux 3.2.0, and Mac OS X 10.6.8, when signal handlers are installed with the signal function, interrupted system calls will be restarted. By using our own implementation of the signal function, we avoid having to deal with these differences. [p329]

One reason 4.2BSD introduced the automatic restart feature is that sometimes we don’t know that the input or output device is a slow device. [p329]

The figure below summarizes the signal functions and their semantics provided by the various implementations.

Figure 10.3 Features provided by various signal implementations

Later this chapter, we provide our own version of the signal function that automatically tries to restart interrupted system calls (other than for the SIGALRM signal), and signal_intr, that tries to never do the restart.

Reentrant Functions

When a signal that is being caught is handled by a process, the normal sequence of instructions being executed by the process is temporarily interrupted by the signal handler. The process then continues executing, but the instructions in the signal handler are now executed. If the signal handler returns (instead of calling exit or longjmp), then the normal sequence of instructions that the process was executing when the signal was caught continues executing. This is similar to what happens when a hardware interrupt occurs.

However, in the signal handler, we can’t tell where the process was executing when the signal was caught:

The Single UNIX Specification specifies the functions that are guaranteed to be safe to call from within a signal handler. These functions are reentrant and are called async-signal safe by the SUS. Besides being reentrant, they block any signals during operation if delivery of a signal might cause inconsistencies.

The following table lists these async-signal safe functions, which are reentrant functions that may be called from a signal handler.

abort faccessat linkat select socketpair
accept fchmod listen sem_post stat
access fchmodat lseek send symlink
aio_error fchown lstat sendmsg symlinkat
aio_return fchownat mkdir sendto tcdrain
aio_suspend fcntl mkdirat setgid tcflow
alarm fdatasync mkfifo setpgid tcflush
bind fexecve mkfifoat setsid tcgetattr
cfgetispeed fork mknod setsockopt tcgetpgrp
cfgetospeed fstat mknodat setuid tcsendbreak
cfsetispeed fstatat open shutdown tcsetattr
cfsetospeed fsync openat sigaction tcsetpgrp
chdir ftruncate pause sigaddset time
chmod futimens pipe sigdelset timer_getoverrun
chown getegid poll sigemptyset timer_gettime
clock_gettime geteuid posix_trace_event sigfillset timer_settime
close getgid pselect sigismember times
connect getgroups raise signal umask
creat getpeername read sigpause uname
dup getpgrp readlink sigpending unlink
dup2 getpid readlinkat sigprocmask unlinkat
execl getppid recv sigqueue utime
execle getsockname recvfrom sigset utimensat
execv getsockopt recvmsg sigsuspend utimes
execve getuid rename sleep wait
_Exit kill renameat sockatmark waitpid
_exit link rmdir socket write

Most of the functions that are not included in table above are missing because:

Note when using the functions in the table above:

Example of calling getpwnam from a signal handler *

The code below shows a program that calls the nonreentrant function getpwnam from a signal handler that is called every second. We use the alarm function (Section 10.10) here to generate a SIGALRM signal every second.

signals/reenter.c

#include "apue.h"
#include <pwd.h>

static void
my_alarm(int signo)
{
    struct passwd   *rootptr;

    printf("in signal handler\n");
    if ((rootptr = getpwnam("root")) == NULL)
            err_sys("getpwnam(root) error");
    alarm(1);
}

int
main(void)
{
    struct passwd   *ptr;

    signal(SIGALRM, my_alarm);
    alarm(1);
    for ( ; ; ) {
        if ((ptr = getpwnam("sar")) == NULL)
            err_sys("getpwnam error");
        if (strcmp(ptr->pw_name, "sar") != 0)
            printf("return value corrupted!, pw_name = %s\n",
                    ptr->pw_name);
    }
}

When this program was run, the results were random. Usually, the program would be terminated by a SIGSEGV signal when the signal handler returned after several iterations. An examination of the core file showed that the main function had called getpwnam, but that when getpwnam called free, the signal handler interrupted it and called getpwnam, which in turn called free. The data structures maintained by malloc and free had been corrupted when the signal handler (indirectly) called free while the main function was also calling free. Occasionally, the program would run for several seconds before crashing with a SIGSEGV error. When the main function did run correctly after the signal had been caught, the return value was sometimes corrupted and sometimes fine.

SIGCLD Semantics

Two signals that continually generate confusion are SIGCLD and SIGCHLD. The name SIGCLD (without the H) is from System V, and this signal has different semantics from the BSD signal, named SIGCHLD. The POSIX.1 signal is also named SIGCHLD.

The semantics of the BSD SIGCHLD signal are normal and its semantics are similar to all other signals. When the signal occurs, the status of a child has changed, and we need to call one of the wait functions to determine what has happened.

System V, however, has traditionally handled the SIGCLD signal differently from other signals:

  1. If the process specifically sets its disposition to SIG_IGN, children of the calling process will not generate zombie processes. [p333]
    • 4.4BSD always generates zombies if SIGCHLD is ignored. If we want to avoid zombies, we have to wait for our children.
  2. If we set the disposition of SIGCLD to be caught, the kernel immediately checks whether any child processes are ready to be waited for and, if so, calls the SIGCLD handler. [p333-335]
    • FreeBSD 8.0 and Mac OS X 10.6.8 don’t exhibit this problem, because BSD-based systems generally don’t support historical System V semantics for SIGCLD.
    • Linux 3.2.0 also doesn’t exhibit this problem, because it doesn’t call the SIGCHLD signal handler when a process arranges to catch SIGCHLD and child processes are ready to be waited for, even though SIGCLD and SIGCHLD are defined to be the same value.
    • Solaris avoids this problem by including extra code in the kernel.

Of the four platforms described in this text, only Linux 3.2.0 and Solaris 10 define SIGCLD. On these platforms, SIGCLD is equivalent to SIGCHLD.

Reliable-Signal Terminology and Semantics

This section defines some terms used through the discussion of signals.

POSIX.1 allows the system to deliver the signal either once or more than once in case a blocked signal is generated more than once before the process unblocks the signal. If the system delivers the signal more than once, we say that the signals are queued. Most UNIX systems, however, do not queue signals unless they support the real-time extensions to POSIX.1. Instead, the UNIX kernel simply delivers the signal once. [p336]

Section 10.20 discusses queueing signals further.

POSIX.1 does not specify the order in which the signals are delivered to the process. The Rationale for POSIX.1 does suggest, however, that signals related to the current state of the process be delivered before other signals. (SIGSEGV is one such signal.)

Each process has a signal mask that defines the set of signals currently blocked from delivery to that process. This mask has one bit for each possible signal. If the bit is on for a given signal, that signal is currently blocked. A process can examine and change its current signal mask by calling sigprocmask (Section 10.12). Since it is possible for the number of signals to exceed the number of bits in an integer, POSIX.1 defines a data type, called sigset_t, that holds a signal set. The signal mask is stored in one of these signal sets. The five functions that operate on signal sets are described in Section 10.11.

kill and raise Functions

#include <signal.h>

int kill(pid_t pid, int signo);
int raise(int signo);

/* Both return: 0 if OK, −1 on error */

The call:

raise(signo);

is equivalent to the call:

kill(getpid(), signo);

There are four different conditions for the pid argument to kill:

Note that the term all processes in the four conditions above excludes an implementation-defined set of system processes, including kernel processes and init (pid 1).

A process needs permission to send a signal to another process:

Some other notes on the kill function:

alarm and pause Functions

The alarm function sets a timer that will expire at a specified time in the future. When the timer expires, the SIGALRM signal is generated. If we ignore or don’t catch this signal, its default action is to terminate the process.

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

/* Returns: 0 or number of seconds until previously set alarm */

Although the default action for SIGALRM is to terminate the process, most processes that use an alarm clock catch this signal, which can perform whatever cleanup is required before terminating if the process wants to terminate. If we intend to catch SIGALRM, we need to be careful to install its signal handler before calling alarm. If we call alarm first and are sent SIGALRM before we can install the signal handler, our process will terminate.

The pause function suspends the calling process until a signal is caught.

#include <unistd.h>

int pause(void);

/* Returns: −1 with errno set to EINTR */

The only time pause returns is if a signal handler is executed and that handler returns. In that case, pause returns −1 with errno set to EINTR.

sleep1 example

Using alarm and pause, we can put a process to sleep for a specified amount of time. The following implementation of sleep1 is incomplete and has problems:

signals/sleep1.c

#include    <signal.h>
#include    <unistd.h>

static void
sig_alrm(int signo)
{
    /* nothing to do, just return to wake up the pause */
}

unsigned int
sleep1(unsigned int seconds)
{
    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
        return(seconds);
    alarm(seconds);     /* start the timer */
    pause();            /* next caught signal wakes us up */
    return(alarm(0));   /* turn off timer, return unslept time */
}

This simple implementation has three problems:

  1. If the caller already has an alarm set, that alarm is erased by the first call to alarm. We can correct this by looking at alarm’s return value:
    • If the number of seconds until some previously set alarm is less than the argument, then we should wait only until the existing alarm expires.
    • If the previously set alarm will go off after ours, then before returning we should reset this alarm to occur at its designated time in the future.
  2. We have modified the disposition for SIGALRM. If we’re writing a function for others to call, we should save the disposition when our function is called and restore it when we’re done. We can correct this by saving the return value from signal and resetting the disposition before our function returns.
  3. There is a race condition between the first call to alarm and the call to pause. On a busy system, it’s possible for the alarm to go off and the signal handler to be called before we call pause. If that happens, the caller is suspended forever in the call to pause (assuming that some other signal isn’t caught).

sleep2 example: using setjmp and longjmp

The following example corrects problem 3 as described above using setjmp. The problem 3 can also be corrected using sigprocmask and sigsuspend, as described in Section 10.19.

signals/sleep2.c

#include    <setjmp.h>
#include    <signal.h>
#include    <unistd.h>

static jmp_buf  env_alrm;

static void
sig_alrm(int signo)
{
    longjmp(env_alrm, 1);
}

unsigned int
sleep2(unsigned int seconds)
{
    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
        return(seconds);
    if (setjmp(env_alrm) == 0) {
        alarm(seconds);     /* start the timer */
        pause();            /* next caught signal wakes us up */
    }
    return(alarm(0));       /* turn off timer, return unslept time */
}

The sleep2 function avoids the race condition. Even if the pause is never executed, the sleep2 function returns when the SIGALRM occurs.

sleep2's interaction with other signals

There is another subtle problem with the sleep2 function involving its interaction with other signals. If the SIGALRM interrupts some other signal handler, then when we call longjmp, we abort the other signal handler, as shown in the following code:

signals/tsleep2.c

#include "apue.h"

unsigned int    sleep2(unsigned int);
static void     sig_int(int);

int
main(void)
{
    unsigned int    unslept;

    if (signal(SIGINT, sig_int) == SIG_ERR)
        err_sys("signal(SIGINT) error");
    unslept = sleep2(5);
    printf("sleep2 returned: %u\n", unslept);
    exit(0);
}

static void
sig_int(int signo)
{
    int             i, j;
    volatile int    k;
/*
     * Tune these loops to run for more than 5 seconds
     * on whatever system this test program is run.
     */
    printf("\nsig_int starting\n");
    for (i = 0; i < 300000; i++)
        for (j = 0; j < 4000; j++)
            k += i * j;
    printf("sig_int finished\n");
}

The loop in the SIGINT handler was written so that it executes for longer than 5 seconds on one of the systems used by the author. We simply want it to execute longer than the argument to sleep2. The integer k is declared as volatile to prevent an optimizing compiler from discarding the loop.

Run this program and interrupt the sleep by typing the interrupt character:

$ ./a.out
ˆC                 # we type the interrupt character
sig_int starting
sleep2 returned: 0

The longjmp from the sleep2 function aborted the other signal handler, sig_int, even though it wasn’t finished.

The above examples of sleep1 and sleep2 show the pitfalls in dealing naively with signals. The following sections will show ways around all these problems, so we can handle signals reliably, without interfering with other pieces of code.

Implementing a timeout using alarm

A common use for alarm, in addition to implementing the sleep function, is to put an upper time limit on operations that can block. For example, if we have a read operation on a device that can block (slow device, as described in Section 10.5), we might want the read to time out after some amount of time. The following example reads one line from standard input (with a timeout) and writes it to standard.

signals/read1.c

#include "apue.h"

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);
    if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
        err_sys("read error");
    alarm(0);

    write(STDOUT_FILENO, line, n);
    exit(0);
}

static void
sig_alrm(int signo)
{
    /* nothing to do, just return to interrupt the read */
}

Though this code is common in UNIX applications, it has two problems:

  1. There is a a race condition between the first call to alarm and the call to read, similar to the first alarm and pause example in early this section. If the kernel blocks the process between these two function calls for longer than the alarm period, the read could block forever, though most operations of this type use a long alarm period (a minute or more) making this unlikely.
  2. If system calls are automatically restarted, the read is not interrupted when the SIGALRM signal handler returns. In this case, the timeout does nothing.

Implementing a timeout with alarm and longjmp

signals/read2.c

#include "apue.h"
#include <setjmp.h>

static void     sig_alrm(int);
static jmp_buf  env_alrm;

int
main(void)
{
    int     n;
    char    line[MAXLINE];

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");
    if (setjmp(env_alrm) != 0)
        err_quit("read timeout");

    alarm(10);
    if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
        err_sys("read error");
    alarm(0);

    write(STDOUT_FILENO, line, n);
    exit(0);
}

static void
sig_alrm(int signo)
{
    longjmp(env_alrm, 1);
}

This version works as expected, regardless of whether the system restarts interrupted system calls. However, we still have the problem of interactions with other signal handlers, as described previously.

If we want to set a time limit on an I/O operation, we need to use longjmp, as shown previously, while recognizing its possible interaction with other signal handlers. Another option is to use the select or poll functions described in Section 14.4.

Signal Sets

A signal set is a data type to represent multiple signals. This data type is used with functions like sigprocmask to tell the kernel not to allow any of the signals in the set to occur. As mentioned earlier, the number of different signals can exceed the number of bits in an integer, so in general we can’t use an integer to represent the set with one bit per signal.

POSIX.1 defines the data type sigset_t to contain a signal set and the following five functions to manipulate signal sets.

#include <signal.h>

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 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 */

All applications have to call either sigemptyset or sigfillset once for each signal set, before using the signal set, because we cannot assume that the C initialization for external and static variables (0) corresponds to the implementation of signal sets on a given system.

In all the functions that take a signal set as an argument, we always pass the address of the signal set as the argument.

Implementation of signal sets

If the implementation has fewer signals than bits in an integer,asignal set can be implemented using one bit per signal. This section assumes that an implementation has 31 signals and 32-bit integers. The sigemptyset function zeros the integer, and the sigfillset function turns on all the bits in the integer. These two functions can be implemented as macros in the <signal.h> header:

#define sigemptyset(ptr) (*(ptr) = 0)
#define sigfillset(ptr) (*(ptr) = ~(sigset_t)0, 0)

Note that sigfillset must return 0, in addition to setting all the bits on in the signal set, so we use C’s comma operator, which returns the value after the comma as the value of the expression.

Using this implementation, sigaddset turns on a single bit and sigdelset turns off a single bit; sigismember tests a certain bit. Since no signal is ever numbered 0, we subtract 1 from the signal number to obtain the bit to manipulate.

signals/setops.c

#include    <signal.h>
#include    <errno.h>

/*
 * <signal.h> usually defines NSIG to include signal number 0.
 */
#define SIGBAD(signo)   ((signo) <= 0 || (signo) >= NSIG)

int
sigaddset(sigset_t *set, int signo)
{
    if (SIGBAD(signo)) {
        errno = EINVAL;
        return(-1);
    }
    *set |= 1 << (signo - 1);       /* turn bit on */
    return(0);
}

int
sigdelset(sigset_t *set, int signo)
{
    if (SIGBAD(signo)) {
        errno = EINVAL;
        return(-1);
    }
    *set &= ~(1 << (signo - 1));    /* turn bit off */
    return(0);
}

int
sigismember(const sigset_t *set, int signo)
{
    if (SIGBAD(signo)) {
        errno = EINVAL;
        return(-1);
    }
    return((*set & (1 << (signo - 1))) != 0);
}

sigprocmask Function

As discussed in Section 10.8, the signal mask of a process is the set of signals currently blocked from delivery to that process. A process can examine its signal mask, change its signal mask, or perform both operations in one step by calling the following function.

#include <signal.h>

int sigprocmask(int how, const sigset_t *restrict set,
                sigset_t *restrict oset);

/* Returns: 0 if OK, −1 on error */

After calling sigprocmask, if any unblocked signals are pending, at least one of these signals is delivered to the process before sigprocmask returns.

Note that the sigprocmask function is defined only for single-threaded processes. A separate function, discussed in Section 12.8 is provided to manipulate a thread’s signal mask in a multithreaded process.

The following example shows a function that prints the names of the signals in the signal mask of the calling process.

lib/prmask.c

#include "apue.h"
#include <errno.h>

void
pr_mask(const char *str)
{
    sigset_t    sigset;
    int         errno_save;

    errno_save = errno;     /* we can be called by signal handlers */
    if (sigprocmask(0, NULL, &sigset) < 0) {
        err_ret("sigprocmask error");
    } else {
        printf("%s", str);
        if (sigismember(&sigset, SIGINT))
            printf(" SIGINT");
        if (sigismember(&sigset, SIGQUIT))
            printf(" SIGQUIT");
        if (sigismember(&sigset, SIGUSR1))
            printf(" SIGUSR1");
        if (sigismember(&sigset, SIGALRM))
            printf(" SIGALRM");

        /* remaining signals can go here  */

        printf("\n");
    }

    errno = errno_save;     /* restore errno */
}

sigpending Function

The sigpending function returns the set of signals that are blocked from delivery and currently pending for the calling process. The set of signals is returned through the set argument.

#include <signal.h>

int sigpending(sigset_t *set);

/* Returns: 0 if OK, −1 on error */

Example of sigpending and other signal features

The example below shows many of the signal features that have been described.

signals/critical.c

#include "apue.h"

static void sig_quit(int);

int
main(void)
{
    sigset_t    newmask, oldmask, pendmask;

    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
        err_sys("can't catch SIGQUIT");

    /*
     * Block SIGQUIT and save current signal mask.
     */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

    sleep(5);   /* SIGQUIT here will remain pending */

    if (sigpending(&pendmask) < 0)
        err_sys("sigpending error");
    if (sigismember(&pendmask, SIGQUIT))
        printf("\nSIGQUIT pending\n");

    /*
     * Restore signal mask which unblocks SIGQUIT.
     */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
    printf("SIGQUIT unblocked\n");

    sleep(5);   /* SIGQUIT here will terminate with core file */
    exit(0);
}

static void
sig_quit(int signo)
{
    printf("caught SIGQUIT\n");
    if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
        err_sys("can't reset SIGQUIT");
}

Run this program:

$ ./a.out
ˆ\                    # generate signal once (before 5 seconds are up)
SIGQUIT               # pending after return from sleep
caught SIGQUIT        # in signal handler
SIGQUIT unblocked     # after return from sigprocmask
ˆ\Quit(coredump)      # generate signal again
$ ./a.out
ˆ\ˆ\ˆ\ˆ\ˆ\ˆ\ˆ\ˆ\ˆ\ˆ\  # generate signal 10 times (before 5 seconds are up)
SIGQUIT pending
caught SIGQUIT        # signal is generated only once
SIGQUIT unblocked
ˆ\Quit(coredump)      # generate signal again

[p349]

Some notes from this example:

sigaction Function

The sigaction function allows us to examine or modify (or both) the action associated with a particular signal. This function supersedes the signal function from earlier releases of the UNIX System. Indeed, at the end of this section, we show an implementation of signal using sigaction.

#include <signal.h>

int sigaction(int signo, const struct sigaction *restrict act,
              struct sigaction *restrict oact);

/* Returns: 0 if OK, −1 on error */

This function uses the following structure:

struct sigaction {
    void (*sa_handler)(int);  /* addr of signal handler, */
                              /* or SIG_IGN, or SIG_DFL */
    sigset_t sa_mask;         /* additional signals to block */
    int sa_flags;             /* signal options, Figure 10.16 */

    /* alternate handler */
    void (*sa_sigaction)(int, siginfo_t *, void *);
};

If the sa_handler field contains the address of a signal-catching function (rather than SIG_IGN or SIG_DFL), then the sa_mask field specifies a set of signals that are added to the signal mask of the process before the signal-catching function is called. If and when the signal-catching function returns, the signal mask of the process is reset to its previous value. This enables us to block certain signals whenever a signal handler is invoked. The operating system includes the signal being delivered in the signal mask when the handler is invoked. Hence, we are guaranteed that whenever we are processing a given signal, another occurrence of that same signal is blocked until we’re finished processing the first occurrence. Additional occurrences of the same signal are usually not queued (Section 10.8). If the signal occurs five times while it is blocked, when we unblock the signal, the signal-handling function for that signal will usually be invoked only one time.

Once we install an action for a given signal, that action remains installed until we explicitly change it by calling sigaction. [p350]

The sa_flags field of the act structure specifies various options for the handling of this signal. The table below details the meaning of these options when set.

The sa_sigaction field is an alternative signal handler used when the SA_SIGINFO flag is used with sigaction. Implementations might use the same storage for both the sa_sigaction field and the sa_handler field, so applications can use only one of these fields at a time.

Option flags (sa_flags) for the handling of each signal:

Option SUS FreeBSD Linux Mac OS X Solaris Description
SA_INTERRUPT x System calls interrupted by this signal are not automatically restarted (the XSI default for sigaction). See Section 10.5.
SA_NOCLDSTOP x x x x x If signo is SIGCHLD, do not generate this signal when a child process stops (job control). This signal is still generated, of course, when a child terminates (but see the SA_NOCLDWAIT option below). When the XSI option is supported, SIGCHLD won’t be sent when a stopped child continues if this flag is set.
SA_NOCLDWAIT x x x x x If signo is SIGCHLD, this option prevents the system from creating zombie processes when children of the calling process terminate. If it subsequently calls wait, the calling process blocks until all its child processes have terminated and then returns −1 with errno set to ECHILD. (Section 10.7)
SA_NODEFER x x x x x When this signal is caught, the signal is not automatically blocked by the system while the signal-catching function executes (unless the signal is also included in sa_mask). Note that this type of operation corresponds to the earlier unreliable signals.
SA_ONSTACK XSI x x x x If an alternative stack has been declared with sigaltstack(2), this signal is delivered to the process on the alternative stack.
SA_RESETHAND x x x x x The disposition for this signal is reset to SIG_DFL, and the SA_SIGINFO flag is cleared on entry to the signal-catching function. Note that this type of operation corresponds to the earlier unreliable signals. The disposition for the two signals SIGILL and SIGTRAP can’t be reset automatically, however. Setting this flag can optionally cause sigaction to behave as if SA_NODEFER is also set.
SA_RESTART x x x x x System calls interrupted by this signal are automatically restarted. (Section 10.5)
SA_SIGINFO x x x x x This option provides additional information to a signal handler: a pointer to a siginfo structure and a pointer to an identifier for the process context.

Normally, the signal handler is called as:

void handler(int signo);

If the SA_SIGINFO flag is set, the signal handler is called as:

void handler(int signo, siginfo_t *info, void *context);

The siginfo structure contains information about why the signal was generated. All POSIX.1-compliant implementations must include at least the si_signo and si_code members. Additionally, implementations that are XSI compliant contain at least the following fields:

struct siginfo {
    int si_signo; /* signal number */
    int si_errno; /* if nonzero, errno value from errno.h */
    int si_code; /* additional info (depends on signal) */
    pid_t si_pid; /* sending process ID */
    uid_t si_uid; /* sending process real user ID */
    void *si_addr; /* address that caused the fault */
    int si_status; /* exit value or signal number */
    union sigval si_value; /* application-specific value */
    /* possibly other fields also */
};

The sigval union contains the following fields:

int sival_int;
void *sival_ptr;

Applications pass an integer value in si_value.sival_int or pass a pointer value in si_value.sival_ptr when delivering signals.

If the signal is SIGCHLD, then the si_pid, si_status, and si_uid fields will be set. If the signal is SIGBUS, SIGILL, SIGFPE, or SIGSEGV, then the si_addr contains the address responsible for the fault. The si_errno field contains the error number corresponding to the condition that caused the signal to be generated.

The table shows values of si_code for various signals, as defined by the Single UNIX Specification:

Figure 10.17 siginfo_t code values

The context argument to the signal handler is a typeless pointer that can be cast to a ucontext_t structure identifying the process context at the time of signal delivery. This structure contains at least the following fields:

ucontext_t *uc_link;    /* pointer to context resumed when */
                        /* this context returns */
sigset_t uc_sigmask;    /* signals blocked when this context */
                        /* is active */
stack_t uc_stack;       /* stack used by this context */
mcontext_t uc_mcontext; /* machine-specific representation of */
                        /* saved context */

The uc_stack field describes the stack used by the current context. It contains at least the following members:

void *ss_sp; /* stack base or pointer */
size_t ss_size; /* stack size */
int ss_flags; /* flags */

When an implementation supports the real-time signal extensions, signal handlers established with the SA_SIGINFO flag will result in signals being queued reliably. A separate range of reserved signal numbers is available for real-time application use. Applications can pass information along with the signal by using the sigqueue function (Section 10.20).

Example: signal Function

The following is the implementation of the signal function using sigaction. [p354]

lib/signal.c

#include "apue.h"

/* Reliable version of signal(), using POSIX sigaction().  */
Sigfunc *
signal(int signo, Sigfunc *func)
{
    struct sigaction    act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (signo == SIGALRM) {
#ifdef  SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    } else {
        act.sa_flags |= SA_RESTART;
    }
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

Example: signal_intr Function

The following code shows a version of the signal function that tries to prevent any interrupted system calls from being restarted.

lib/signalintr.c

#include "apue.h"

Sigfunc *
signal_intr(int signo, Sigfunc *func)
{
    struct sigaction    act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef  SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

For improved portability, we specify the SA_INTERRUPT flag, if defined by the system, to prevent interrupted system calls from being restarted.

sigsetjmp and siglongjmp Functions

setjmp and longjmp functions (Section 7.10) can be used for nonlocal branching. The longjmp function is often called from a signal handler to return to the main loop of a program, instead of returning from the handler. (Figure 10.8 and Figure 10.11).

However, there is a problem in calling longjmp. When a signal is caught, the signal-catching function is entered, with the current signal automatically being added to the signal mask of the process. This prevents subsequent occurrences of that signal from interrupting the signal handler. If we longjmp out of the signal handler, what happens to the signal mask for the process depends on the platform:

To allow either form of behavior, POSIX.1 does not specify the effect of setjmp and longjmp on signal masks. Instead, two new functions, sigsetjmp and siglongjmp, are defined by POSIX.1. These two functions should always be used when branching from a signal handler.

#include <setjmp.h>

int sigsetjmp(sigjmp_buf env, int savemask);
/* Returns: 0 if called directly, nonzero if returning from a call to siglongjmp */

void siglongjmp(sigjmp_buf env, int val);

The only difference between these functions and the setjmp and longjmp functions is that sigsetjmp has an additional argument. If savemask is nonzero, then sigsetjmp also saves the current signal mask of the process in env. When siglongjmp is called, if the env argument was saved by a call to sigsetjmp with a nonzero savemask, then siglongjmp restores the saved signal mask.

signals/mask.c

#include "apue.h"
#include <setjmp.h>
#include <time.h>

static void                     sig_usr1(int);
static void                     sig_alrm(int);
static sigjmp_buf               jmpbuf;
static volatile sig_atomic_t    canjump;

int
main(void)
{
    if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
        err_sys("signal(SIGUSR1) error");
    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");

    pr_mask("starting main: ");     /* {Prog prmask} */

    if (sigsetjmp(jmpbuf, 1)) {

        pr_mask("ending main: ");

        exit(0);
    }
    canjump = 1;    /* now sigsetjmp() is OK */

    for ( ; ; )
        pause();
}

static void
sig_usr1(int signo)
{
    time_t  starttime;

    if (canjump == 0)
        return;     /* unexpected signal, ignore */

    pr_mask("starting sig_usr1: ");

    alarm(3);               /* SIGALRM in 3 seconds */
    starttime = time(NULL);
    for ( ; ; )             /* busy wait for 5 seconds */
        if (time(NULL) > starttime + 5)
            break;

    pr_mask("finishing sig_usr1: ");

    canjump = 0;
    siglongjmp(jmpbuf, 1);  /* jump back to main, don't return */
}

static void
sig_alrm(int signo)
{
    pr_mask("in sig_alrm: ");
}

We can divide the following figure into three parts:

While the process is executing in the left part, its signal mask is 0 (no signals are blocked). While executing in the center part, its signal mask is SIGUSR1. While executing in the right part, its signal mask is SIGUSR1|SIGALRM.

Figure 10.21 Timeline for example program handling two signals

The output of the program:

$ ./a.out &             # start process in background
starting main:
[1] 531                 # the job-control shell prints its process ID
$ kill -USR1 531        # send the process SIGUSR1
starting sig_usr1: SIGUSR1
$ in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:
                        # just press RETURN
[1] + Done ./a.out &

The output is what we expect: when a signal handler is invoked, the signal being caught is added to the current signal mask of the process. The original mask is restored when the signal handler returns. Also, siglongjmp restores the signal mask that was saved by sigsetjmp.

sigsuspend Function

We have seen how we can change the signal mask for a process to block and unblock selected signals. We can use this technique to protect critical regions of code that we don’t want interrupted by a signal. But what if we want to unblock a signal and then pause, waiting for the previously blocked signal to occur? Assuming that the signal is SIGINT, the incorrect way to do this is:

  sigset_t newmask, oldmask;

  sigemptyset(&newmask);
  sigaddset(&newmask, SIGINT);

  /* block SIGINT and save current signal mask */
  if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
      err_sys("SIG_BLOCK error");

  /* critical region of code */

  /* restore signal mask, which unblocks SIGINT */
  if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
      err_sys("SIG_SETMASK error");

  /* window is open */
  pause(); /* wait for signal to occur */

  /* continue processing */

If the signal is sent to the process while it is blocked, the signal delivery will be deferred until the signal is unblocked. To the application, this can look as if the signal occurs between the unblocking and the pause (depending on how the kernel implements signals). If this happens, or if the signal does occur between the unblocking and the pause, we have a problem. Any occurrence of the signal in this window of time is lost, in the sense that we might not see the signal again, in which case the pause will block indefinitely. This is another problem with the earlier unreliable signals.

To correct this problem, we need a way to both restore the signal mask and put the process to sleep in a single atomic operation. This feature is provided by the sigsuspend function.

#include <signal.h>

int sigsuspend(const sigset_t *sigmask);

/* Returns: −1 with errno set to EINTR */

The signal mask of the process is set to the value pointed to by sigmask. Then the process is suspended until a signal is caught or until a signal occurs that terminates the process. If a signal is caught and if the signal handler returns, then sigsuspend returns, and the signal mask of the process is set to its value before the call to sigsuspend.

Example of sigsuspend to protect a critial region

The following code shows the correct way to protect a critical region of code from a specific signal.

signals/suspend1.c

#include "apue.h"

static void sig_int(int);

int
main(void)
{
    sigset_t    newmask, oldmask, waitmask;

    pr_mask("program start: ");

    if (signal(SIGINT, sig_int) == SIG_ERR)
        err_sys("signal(SIGINT) error");
    sigemptyset(&waitmask);
    sigaddset(&waitmask, SIGUSR1);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGINT);

    /*
     * Block SIGINT and save current signal mask.
     */
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

    /*
     * Critical region of code.
     */
    pr_mask("in critical region: ");

    /*
     * Pause, allowing all signals except SIGUSR1.
     */
    if (sigsuspend(&waitmask) != -1)
        err_sys("sigsuspend error");

    pr_mask("after return from sigsuspend: ");

    /*
     * Reset signal mask which unblocks SIGINT.
     */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");

    /*
     * And continue processing ...
     */
    pr_mask("program exit: ");

    exit(0);
}

static void
sig_int(int signo)
{
    pr_mask("\nin sig_int: ");
}

When sigsuspend returns, it sets the signal mask to its value before the cal SIGINT signal will be blocked, so we restore the signal mask to the value that we saved earlier (oldmask).

Running the program produces the following output:

$ ./a.out
program start:
in critical region: SIGINT
ˆC                          # type the interrupt character
in sig_int: SIGINT SIGUSR1
after return from sigsuspend: SIGINT
program exit:

Example of sigsuspend to wait for a signal handler to set a global variable

In the following program, we catch both the interrupt signal and the quit signal, but want to wake up the main routine only when the quit signal is caught.

signals/suspend2.c

#include "apue.h"

volatile sig_atomic_t   quitflag;   /* set nonzero by signal handler */

static void
sig_int(int signo)  /* one signal handler for SIGINT and SIGQUIT */
{
    if (signo == SIGINT)
        printf("\ninterrupt\n");
    else if (signo == SIGQUIT)
        quitflag = 1;   /* set flag for main loop */
}

int
main(void)
{
    sigset_t    newmask, oldmask, zeromask;

    if (signal(SIGINT, sig_int) == SIG_ERR)
        err_sys("signal(SIGINT) error");
    if (signal(SIGQUIT, sig_int) == SIG_ERR)
        err_sys("signal(SIGQUIT) error");

    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);

    /*
     * Block SIGQUIT and save current signal mask.
     */
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

    while (quitflag == 0)
        sigsuspend(&zeromask);

    /*
     * SIGQUIT has been caught and is now blocked; do whatever.
     */
    quitflag = 0;

    /*
     * Reset signal mask which unblocks SIGQUIT.
     */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");

    exit(0);
}

Sample output from this program is:

$ ./a.out
ˆC           # type the interrupt character
interrupt
ˆC           # type the interrupt character again
interrupt
ˆC           # and again
interrupt
ˆ\ $         # now terminate with the quit character

Example of signals that synchronize a parent and child

This example shows how signals can be used to synchronize a parent and child. The following example shows implementations of the five routines TELL_WAIT, TELL_PARENT, TELL_CHILD, WAIT_PARENT, and WAIT_CHILD from Section 8.9.

lib/tellwait.c

#include "apue.h"

static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;

static void
sig_usr(int signo)  /* one signal handler for SIGUSR1 and SIGUSR2 */
{
    sigflag = 1;
}

void
TELL_WAIT(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
        err_sys("signal(SIGUSR1) error");
    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
        err_sys("signal(SIGUSR2) error");
    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    sigaddset(&newmask, SIGUSR2);

    /* Block SIGUSR1 and SIGUSR2, and save current signal mask */
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");
}

void
TELL_PARENT(pid_t pid)
{
    kill(pid, SIGUSR2);     /* tell parent we're done */
}

void
WAIT_PARENT(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for parent */
    sigflag = 0;

    /* Reset signal mask to original value */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

void
TELL_CHILD(pid_t pid)
{
    kill(pid, SIGUSR1);         /* tell child we're done */
}

void
WAIT_CHILD(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for child */
    sigflag = 0;

    /* Reset signal mask to original value */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

In the example, two user-defined signals are used: SIGUSR1 is sent by the parent to the child, and SIGUSR2 is sent by the child to the parent.

The sigsuspend function is fine if we want to go to sleep while we’re waiting for a signal to occur. If we want to call other system functions while we’re waiting, the only solution is to use multiple threads and dedicate a separate thread to handling signals (Section 12.8).

Without using threads, the best we can do is to set a global variable in the signal handler when the signal occurs. For example, if we catch both SIGINT and SIGALRM and install the signal handlers using the signal_intr function, the signals will interrupt any slow system call that is blocked. The signals are most likely to occur when we’re blocked in a call to the read function waiting for input from a slow device. (This is especially true for SIGALRM, since we set the alarm clock to prevent us from waiting forever for input.) The code to handle this looks similar to the following:

    if (intr_flag)      /* flag set by our SIGINT handler */
        handle_intr();
    if (alrm_flag)      /* flag set by our SIGALRM handler */
        handle_alrm();

    /* signals occurring in here are lost */

    while (read( ... ) < 0) {
        if (errno == EINTR) {
            if (alrm_flag)
                handle_alrm();
            else if (intr_flag)
                handle_intr();
        } else {
            /* some other error */
            }
        } else if (n == 0) {
            /* end of file */
        } else {
            /* process input */
    }

We test each of the global flags before calling read and again if read returns an interrupted system call error. The problem occurs if either signal is caught between the first two if statements and the subsequent call to read. Signals occurring in here are lost, as indicated by the code comment. The signal handlers are called, and they set the appropriate global variable, but the read never returns (unless some data is ready to be read).

What we would like to be able to do is the following sequence of steps, in order.

  1. Block SIGINT and SIGALRM.
  2. Test the two global variables to see whether either signal has occurred and, if so, handle the condition.
  3. Call read (or any other system function) and unblock the two signals, as an atomic operation.

The sigsuspend function helps us only if step 3 is a pause operation.

abort Function

The abort function causes abnormal program termination.

#include <stdlib.h>

void abort(void);

/* This function never returns */

The abort function sends the SIGABRT signal to the caller. Processes should not ignore this signal. ISO C states that calling abort will deliver an unsuccessful termination notification to the host environment by calling raise(SIGABRT).

[p365]

ISO C:

POSIX.1:

Since most UNIX System implementations of tmpfile call unlink immediately after creating the file, the ISO C warning about temporary files does not usually concern us. [p366]

The following is an implementation of the abort function as specified by POSIX.1:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void
abort(void)         /* POSIX-style abort() function */
{
    sigset_t            mask;
    struct sigaction    action;

    /* Caller can't ignore SIGABRT, if so reset to default */
    sigaction(SIGABRT, NULL, &action);
    if (action.sa_handler == SIG_IGN) {
        action.sa_handler = SIG_DFL;
        sigaction(SIGABRT, &action, NULL);
    }
    if (action.sa_handler == SIG_DFL)
        fflush(NULL);           /* flush all open stdio streams */

    /* Caller can't block SIGABRT; make sure it's unblocked */
    sigfillset(&mask);
    sigdelset(&mask, SIGABRT);  /* mask has only SIGABRT turned off */
    sigprocmask(SIG_SETMASK, &mask, NULL);
    kill(getpid(), SIGABRT);    /* send the signal */

    /* If we're here, process caught SIGABRT and returned */
    fflush(NULL);               /* flush all open stdio streams */
    action.sa_handler = SIG_DFL;
    sigaction(SIGABRT, &action, NULL);  /* reset to default */
    sigprocmask(SIG_SETMASK, &mask, NULL);  /* just in case ... */
    kill(getpid(), SIGABRT);                /* and one more time */
    exit(1);    /* this should never be executed ... */
}

system Function

Section 8.13 showed an implementation of the system function, which did not do any signal handling. POSIX.1 requires that system ignore SIGINT and SIGQUIT and block SIGCHLD. Before showing a version that handles these signals correctly, let’s see why we need to worry about signal handling.

Example of system invoking ed editor

The program (Figure 10.26) shown below uses the system function from Section 8.13 to invoke the ed(1) editor. It is an interactive program that catches the interrupt and quit signals. If ed is invoked from a shell and type the interrupt character, it catches the interrupt signal and prints a question mark. The ed program also sets the disposition of the quit signal so that it is ignored.

signals/systest2.c

#include "apue.h"

static void
sig_int(int signo)
{
    printf("caught SIGINT\n");
}

static void
sig_chld(int signo)
{
    printf("caught SIGCHLD\n");
}

int
main(void)
{
    if (signal(SIGINT, sig_int) == SIG_ERR)
        err_sys("signal(SIGINT) error");
    if (signal(SIGCHLD, sig_chld) == SIG_ERR)
        err_sys("signal(SIGCHLD) error");
    if (system("/bin/ed") < 0)
        err_sys("system() error");
    exit(0);
}

This program catches both SIGINT and SIGCHLD. If we invoke the program, we get:

$ ./a.out
a                         # append text to the editor’s buffer
Here is one line of text
.                         # period on a line by itself stops append mode
1,$p                      # print first through last lines of buffer to see what’s there
Here is one line of text
w temp.foo                # write the buffer to a file
25                        # editor says it wrote 25 bytes
q                         # and leave the editor
caught SIGCHLD

If we run the program again, this time sending the editor an interrupt signal, we get

$ ./a.out
a                # append text to the editor’s buffer
hello, world
.                # period on a line by itself stops append mode
1,$p             # print first through last lines to see what’s there
hello, world
w temp.foo       # write the buffer to a file
13               # editor says it wrote 13 bytes
ˆC               # type the interrupt character
?                # editor catches signal, prints question mark
caught SIGINT    # and so does the parent process
q                # leave editor
caught SIGCHLD

As mentioned in Section 9.6, typing the interrupt character causes the interrupt signal to be sent to all the processes in the foreground process group. The following figure shows the arrangement of the processes when the editor is running.

Figure 10.27 Foreground and background process groups for Figure 10.26

In this example:

SIGINT is sent to all three foreground processes (the shell ignores it) and both the a.out process and the editor catch the signal. When running another program with the system function, we shouldn’t have both the parent and the child catching the two terminal-generated signals: interrupt and quit. Instead, these two signals should be sent to the program that is running: the child. Since the command that is executed by system can be an interactive command (the ed program in this example) and since the caller of system gives up control while the program executes, waiting for it to finish, the caller of system should not be receiving these two terminal-generated signals. For this reason, POSIX.1 specifies that the system function should ignore the SIGINT and SIGQUIT signals while waiting for the command to complete.

Implementation of system with signal handling

The program below shows an implementation of the system function with the required signal handling.

signals/system.c

#include    <sys/wait.h>
#include    <errno.h>
#include    <signal.h>
#include    <unistd.h>

int
system(const char *cmdstring)   /* with appropriate signal handling */
{
    pid_t               pid;
    int                 status;
    struct sigaction    ignore, saveintr, savequit;
    sigset_t            chldmask, savemask;

    if (cmdstring == NULL)
        return(1);      /* always a command processor with UNIX */

    ignore.sa_handler = SIG_IGN;    /* ignore SIGINT and SIGQUIT */
    sigemptyset(&ignore.sa_mask);
    ignore.sa_flags = 0;
    if (sigaction(SIGINT, &ignore, &saveintr) < 0)
        return(-1);
    if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
        return(-1);
    sigemptyset(&chldmask);         /* now block SIGCHLD */
    sigaddset(&chldmask, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
        return(-1);

    if ((pid = fork()) < 0) {
        status = -1;    /* probably out of processes */
    } else if (pid == 0) {          /* child */
        /* restore previous signal actions & reset signal mask */
        sigaction(SIGINT, &saveintr, NULL);
        sigaction(SIGQUIT, &savequit, NULL);
        sigprocmask(SIG_SETMASK, &savemask, NULL);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);     /* exec error */
    } else {                        /* parent */
        while (waitpid(pid, &status, 0) < 0)
            if (errno != EINTR) {
                status = -1; /* error other than EINTR from waitpid() */
                break;
            }
    }

    /* restore previous signal actions & reset signal mask */
    if (sigaction(SIGINT, &saveintr, NULL) < 0)
        return(-1);
    if (sigaction(SIGQUIT, &savequit, NULL) < 0)
        return(-1);
    if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
        return(-1);

    return(status);
}

This implementation of system differs from the previous flawed one in the following ways:

  1. No signal is sent to the calling process when we type the interrupt or quit character.
  2. When the ed command exits, SIGCHLD is not sent to the calling process. Instead, it is blocked until we unblock it in the last call to sigprocmask, after the system function retrieves the child’s termination status by calling waitpid.

POSIX.1 states that if wait or waitpid returns the status of a child process while SIGCHLD is pending, then SIGCHLD should not be delivered to the process unless the status of another child process is also available. FreeBSD 8.0, Mac OS X 10.6.8, and Solaris 10 all implement this semantic, while Linux 3.2.0 doesn’t. In Linux, SIGCHLD remains pending after the system function calls waitpid; when the signal is unblocked, it is delivered to the caller. If we called wait in the sig_chld function in Figure 10.26, a Linux system would return −1 with errno set to ECHILD, since the system function already retrieved the termination status of the child.

Many older texts show the ignoring of the interrupt and quit signals as follows:

  if ((pid = fork()) < 0) {
      err_sys("fork error");
  } else if (pid == 0) {
    /* child */
    execl(...);
    _exit(127);
  }

  /* parent */
  old_intr = signal(SIGINT, SIG_IGN);
  old_quit = signal(SIGQUIT, SIG_IGN);
  waitpid(pid, &status, 0)
  signal(SIGINT, old_intr);
  signal(SIGQUIT, old_quit);

The problem with this sequence of code is that we have no guarantee after the fork regarding whether the parent or child runs first. If the child runs first and the parent doesn’t run for some time after, an interrupt signal might be generated before the parent is able to change its disposition to be ignored. For this reason, in the implementation of system, we change the disposition of the signals before the fork.

Note that we have to reset the dispositions of these two signals in the child before the call to execl. This allows execl to change their dispositions to the default, based on the caller’s dispositions, as described in Section 8.10.

Return Value from system

The return value from system is the termination status of the shell, which isn’t always the termination status of the command string. [p371]

Run the program in Figure 8.24 and send some signals to the command that’s executing:

$ tsys "sleep 30"
ˆCnormal termination, exit status = 130  # we press the interrupt key
$ tsys "sleep 30"
ˆ\sh: 946                                # Quit we press the quit key
normal termination, exit status = 131

When we terminate the sleep call with the interrupt signal, the pr_exit function (Figure 8.5) thinks that it terminated normally. The same thing happens when we kill the sleep call with the quit key. This is because the Bourne shell has a poorly documented feature in which its termination status is 128 plus the signal number, when the command it was executing is terminated by a signal. [p372]

Try a similar example, but this time we’ll send a signal directly to the shell and see what is returned by system:

$ tsys "sleep 30" &      # start it in background this time
9257
$ ps -f                  # look at the process IDs
UID PID PPID TTY TIME CMD
sar 9260 949 pts/5 0:00 ps -f
sar 9258 9257 pts/5 0:00 sh -c sleep 30
sar 949 947 pts/5 0:01 /bin/sh
sar 9257 949 pts/5 0:00 tsys sleep 30
sar 9259 9258 pts/5 0:00 sleep 30
$ kill -KILL 9258        # kill the shell itself
abnormal termination, signal number = 9

We can see that the return value from system reports an abnormal termination only when the shell itself terminates abnormally.

Other shells behave differently when handling terminal-generated signals, such as SIGINT and SIGQUIT. With bash and dash, for example, pressing the interrupt or quit key will result in an exit status indicating abnormal termination with the corresponding signal number. However, if we find our process executing sleep and send it a signal directly, so that the signal goes only to the individual process instead of the entire foreground process group, we will find that these shells behave like the Bourne shell and exit with a normal termination status of 128 plus the signal number.

When writing programs that use the system function, be sure to interpret the return value correctly. If you call fork, exec, and wait yourself, the termination status is not the same as if you call system.

sleep, nanosleep, and clock_nanosleep Functions

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

/* Returns: 0 or number of unslept seconds */

The sleep function causes the calling process to be suspended until either:

  1. The amount of wall clock time specified by seconds has elapsed. In this case, the return value is 0.
  2. A signal is caught by the process and the signal handler returns. In this case the return value is the number of unslept seconds (the requested time minus the actual time slept).

As with an alarm signal, the actual return may occur at a time later than requested because of other system activity.

Although sleep can be implemented with the alarm function (Section 10.10), this isn’t required. If alarm is used, there can be interactions between the two functions. The POSIX.1 standard leaves all these interactions unspecified.

FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8, and Solaris 10 implement sleep using the nanosleep function, which allows the implementation to be independent of signals and the alarm timer.

The follow example shows an implementation of the POSIX.1 sleep function. This function is a modification of Figure 10.7, which handles signals reliably, avoiding the race condition in the earlier implementation, but does not handle any interactions with previously set alarms.

lib/sleep.c

#include "apue.h"

static void
sig_alrm(int signo)
{
    /* nothing to do, just returning wakes up sigsuspend() */
}

unsigned int
sleep(unsigned int seconds)
{
    struct sigaction    newact, oldact;
    sigset_t            newmask, oldmask, suspmask;
    unsigned int        unslept;

    /* set our handler, save previous information */
    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);

    /* block SIGALRM and save current signal mask */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(seconds);
    suspmask = oldmask;

    /* make sure SIGALRM isn't blocked */
    sigdelset(&suspmask, SIGALRM);

    /* wait for any signal to be caught */
    sigsuspend(&suspmask);

    /* some signal has been caught, SIGALRM is now blocked */

    unslept = alarm(0);

    /* reset previous action */
    sigaction(SIGALRM, &oldact, NULL);

    /* reset signal mask, which unblocks SIGALRM */
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return(unslept);
}

This code doesn't use any form of nonlocal branching (as in Figure 10.8 to avoid the race condition between alarm and pause), so there is no effect on other signal handlers that may be executing when the SIGALRM is handled.

The nanosleep function is similar to the sleep function, but provides nanosecond-level granularity.

#include <time.h>

int nanosleep(const struct timespec *reqtp, struct timespec *remtp);

/* Returns: 0 if slept for requested time or −1 on error */

This function suspends the calling process until either the requested time has elapsed or the function is interrupted by a signal.

Arguments:

Notes on nanosleep:

The clock_nanosleep function provides the capability to suspend the calling thread using a delay time relative to a particular clock, using multiple system clocks (Section 6.10)

#include <time.h>

int clock_nanosleep(clockid_t clock_id, int flags,
                    const struct timespec *reqtp, struct timespec *remtp);

/* Returns: 0 if slept for requested time or error number on failure */

Arguments:

Except for error returns, the call:

clock_nanosleep(CLOCK_REALTIME, 0, reqtp, remtp);

has the same effect as the call:

nanosleep(reqtp, remtp);

Some applications require precision with how long they sleep, and a relative sleep time can lead to sleeping longer than desired. Using an absolute time improves the precision, even though a time-sharing process scheduler makes no guarantee that our task will execute immediately after our sleep time has ended. [p375]

sigqueue Function

As discussed in Section 10.8, most UNIX systems don’t queue signals. With the real-time extensions to POSIX.1, some systems began adding support for queueing signals. With SUSv4, the queued signal functionality has moved from the real-time extensions to the base specification.

These extensions allow applications to pass more information along with the delivery (Section 10.14). This information is embedded in a siginfo structure.

To use queued signals we have to do the following:

  1. Specify the SA_SIGINFO flag when we install a signal handler using the sigaction function. Without this flag, it is left up to the implementation whether the signal is queued.
  2. Provide a signal handler in the sa_sigaction member of the sigaction structure instead of using the usual sa_handler field. Implementations might allow us to use the sa_handler field, but we won’t be able to obtain the extra information sent with the sigqueue function.
  3. Use the sigqueue function to send signals.
#include <signal.h>

int sigqueue(pid_t pid, int signo, const union sigval value)

/* Returns: 0 if OK, −1 on error */

The sigqueue function is similar to the kill function, except that:

Signals can’t be queued infinitely. When the SIGQUEUE_MAX limit (POSIX Limits) is reached, sigqueue can fail with errno set to EAGAIN.

With the real-time signal enhancements, other signal numbers between SIGRTMIN and SIGRTMAX inclusive can be queued, and the default action for these signals is to terminate the process. [p376]

The table below summarizes behavior of queued signals on various platforms:

Behavior SUS FreeBSD Linux Mac OS X Solaris
supports sigqueue x x x x x
queues other signals besides SIGRTMIN to SIGRTMAX optional x x
queues signals even if the caller doesn’t use the SA_SIGINFO optional x x

Job-Control Signals

POSIX.1 considers six to be job-control signals:

SIGCHLD Child process has stopped or terminated.
SIGCONT Continue process, if stopped.
SIGSTOP Stop signal (can’t be caught or ignored).
SIGTSTP Interactive stop signal.
SIGTTIN Read from controlling terminal by background process group member.
SIGTTOU Write to controlling terminal by a background process group member.

The following program demonstrates the normal sequence of code used when a program handles job control.

signals/sigtstp.c

#include "apue.h"

#define BUFFSIZE    1024

static void
sig_tstp(int signo) /* signal handler for SIGTSTP */
{
    sigset_t    mask;

    /* ... move cursor to lower left corner, reset tty mode ... */

    /*
     * Unblock SIGTSTP, since it's blocked while we're handling it.
     */
    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    sigprocmask(SIG_UNBLOCK, &mask, NULL);

    signal(SIGTSTP, SIG_DFL);   /* reset disposition to default */

    kill(getpid(), SIGTSTP);    /* and send the signal to ourself */

    /* we won't return from the kill until we're continued */

    signal(SIGTSTP, sig_tstp);  /* reestablish signal handler */

    /* ... reset tty mode, redraw screen ... */
}

int
main(void)
{
    int     n;
    char    buf[BUFFSIZE];

    /*
     * Only catch SIGTSTP if we're running with a job-control shell.
     */
    if (signal(SIGTSTP, SIG_IGN) == SIG_DFL)
        signal(SIGTSTP, sig_tstp);

    while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");

    if (n < 0)
        err_sys("read error");

    exit(0);
}

This program does the following:

Signal Names and Numbers

This section discusses how to map between signal numbers and names. Some systems provide the array:

extern char *sys_siglist[];

The array index is the signal number, giving a pointer to the character string name of the signal. [p379]

To print the signal's character string in a portable manner, use the psignal function:

#include <signal.h>

void psignal(int signo, const char *msg);

The string msg (which normally includes the name of the program) is output to the standard error, followed by a colon and a space, followed by a description of the signal, followed by a newline. If msg is NULL, then only the description is written to the standard error. This function is similar to perror (Section 1.7).

If you have a siginfo structure from an alternative sigaction signal handler, you can print the signal information with the psiginfo function.

#include <signal.h>

void psiginfo(const siginfo_t *info, const char *msg);

You can use the strsignal function if you only need the string description of the signal. This function is similar to strerror (Section 1.7).

#include <string.h>

char *strsignal(int signo);

/* Returns: a pointer to a string describing the signal */

Summary

An understanding of signal handling is essential to advanced UNIX System programming. This chapter has taken a long and thorough look at UNIX System signals, from previous implementations to the POSIX.1 reliable-signal concept and all the related functions, followed by POSIX.1 abort, system, and sleep functions.

Doubts and Solutions

Verbatim

p324 on some macro constants in <signal.h>

These constants can be used in place of the "pointer to a function that takes an integer argument and returns nothing", the second argument to signal, and the return value from signal. The three values used for these constants need not be −1, 0, and 1. They must be three values that can never be the address of any declarable function. Most UNIX systems use the values shown.

Question: Why is the macro in the form SIG_ERR (void (*)())-1 and the like?

Solution: They are integer that cast into an address which means the "pointer to a function that takes an integer argument and returns nothing". void (*)() tells the compiler to ignore type-checking for the parameters. See:

p359 on unblocking signals

If the signal is sent to the process while it is blocked, the signal delivery will be deferred until the signal is unblocked. To the application, this can look as if the signal occurs between the unblocking and the pause (depending on how the kernel implements signals). If this happens, or if the signal does occur between the unblocking and the pause, we have a problem. Any occurrence of the signal in this window of time is lost, in the sense that we might not see the signal again, in which case the pause will block indefinitely. This is another problem with the earlier unreliable signals.

Question: What is exactly the window? Shouldn't be the unblocked signals delivered to the process? Not fully understood.