Implementation and Portability of signal()

In this section, we show how to implement signal() using sigaction(). The implementation is straightforward, but needs to account for the fact that, historically and across different UNIX implementations, signal() has had different semantics. In particular, early implementations of signals were unreliable, meaning that:

As well as being unreliable, early UNIX implementations did not provide automatic restarting of system calls (i.e., the behavior described for the SA_RESTART flag in Interruption and Restarting of System Calls).

The 4.2BSD reliable signals implementation rectified these limitations, and several other UNIX implementations followed suit. However, the older semantics live on today in the System V implementation of signal(), and even contemporary standards such as SUSv3 and C99 leave these aspects of signal() deliberately unspecified.

Tying the above information together, we implement signal() as shown in Example 22-1. By default, this implementation provides the modern signal semantics. If compiled with -DOLD_SIGNAL, then it provides the earlier unreliable signal semantics and doesn’t enable automatic restarting of system calls.

Example 22-1. An implementation of signal()

signals/signal.c
#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t
signal(int sig, sighandler_t handler)
{
    struct sigaction newDisp, prevDisp;

    newDisp.sa_handler = handler;
    sigemptyset(&newDisp.sa_mask);
#ifdef OLD_SIGNAL
    newDisp.sa_flags = SA_RESETHAND | SA_NODEFER;
#else
    newDisp.sa_flags = SA_RESTART;
#endif

    if (sigaction(sig, &newDisp, &prevDisp) == -1)
        return SIG_ERR;
    else
        return prevDisp.sa_handler;
}
      signals/signal.c