Add Book to My BookshelfPurchase This Book Online

Chapter 10 - Signals

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Reliable Signals
Because of the problems alluded to in the previous section, 4.2BSD, and later SVR3, introduced reliable signals. The reliable signal mechanism makes two major changes: first, signal dispositions are no longer reset when a signal handler is called. The disposition remains the same until the program explicitly changes it. The second change is the introduction of the ability to block a signal for later delivery. The signal is not delivered to the process immediately, but it is not ignored. The system remembers that the signal occurred, and, if the process ever unblocks the signal, delivers it then.
Both Berkeley and System V implemented reliable signals by inventing (different) new system calls. Berkeley also reimplemented the signal call in terms of reliable signals (the examples in the previous section will work correctly on a 4.2BSD or 4.3BSD system). In System V, signal provides the old, unreliable mechanism (which nevertheless is adequate for most needs) for backward compatibility. This is true in SVR4 as well.
In this section, we examine the reliable signal implementation offered by SVR3 and SVR4. The Berkeley reliable signal implementation is discussed at the end of the chapter.
The following paragraphs provide the concepts underlying this implementation.
A signal is generated for a process when the event that causes the signal occurs. When the signal is generated, the operating system usually sets a flag of some sort in the process' state information.
A signal is delivered to a process when the action for that signal is actually taken. During the time between the generation of a signal and the time it is delivered, the signal is said to be pending.
In addition to the default disposition, ignoring a signal, and catching it, a process now also has the option of blocking a signal. If a blocked signal is generated for the process and that signal's disposition is either the default or to catch the signal, then the signal remains pending until the process either unblocks the signal or changes the disposition to ignore the signal. The action for a signal is determined when it is delivered, not when it is generated. This allows the process to change the signal's disposition before accepting its delivery.
If a blocked signal is generated more than once for a process before it is unblocked, the operating system has the option of either queueing the signals, or just delivering a single signal. Most UNIX systems choose the simpler of these approaches, and deliver the signal only once. If more than one signal is pending for a process, there is no specified order in which the signals should be delivered. However, POSIX does suggest that signals relating to the current state of the process (e.g., SIGSEGV) should be delivered first.
Each process has a signal mask that defines the set of signals currently being blocked. The signal mask is simply a set of bits, one for each signal. If the bit is on, the signal is blocked; if it is off, the signal may be delivered.
The sigset Function
The sigset function is the reliable signal mechanism's counterpart to the unreliable signal function:
    #include <signal.h>
    void (*sigset(int sig, void (*disp)(int)))(int);
 NoteIn order to make use of sigset in HP-UX 10.x, the_SVR2 constant must be defined at compile time, and the program must be linked with -lV3.
As with signal, sig specifies the signal whose disposition is to be changed, and disp specifies a pointer to the signal handler function. As with signal, the disp parameter may be given one of the values SIG_DFL or SIG_IGN. It may also be given the value SIG_HOLD, in which case the signal is added to the process' signal mask and its disposition remains unchanged.
When a signal that is being caught is delivered, the operating system adds the signal to the process' signal mask, and then calls the signal handler function. When (if) the handler function returns, the signal mask is restored to its state prior to the delivery of the signal. The signal's disposition is no longer changed by the operating system, as it was with signal. This behavior solves the first problem mentioned in the previous section; the window of vulnerability has been eliminated.
Porting note
Recall from above that Berkeley, when implementing reliable signals, redefined their signal function in terms of the new mechanism. But signal does not provide reliable signals in SVR4; it provides the old, unreliable mechanism. This means that signal-handling code in programs that were written for Berkeley-based systems will not work properly on SVR4.
Fortunately, the sigset function accepts exactly the same arguments that signal does, and has the same return value. This means that, when porting code from Berkeley-based systems to SVR4, it is usually sufficient to add the line
    #define signal sigset
to the top of the program. The only case in which this is not sufficient is when the program is working with SIGCHLD; properly handling that case requires use of the sigaction function, described later in this chapter.
Other Functions
The SVR3 reliable signal mechanism provides several other functions as well:
    #include <signal.h>
    int sighold(int sig);
    int sigrelse(int sig);
    int sigignore(int sig);
    int sigpause(int sig);
The sighold function adds sig to the process' signal mask. The sigrelse function removes sig from the process' signal mask. The sigignore function sets the disposition of sig to SIG_IGN.
The sigpause function removes sig from the calling process' signal mask and then suspends the calling process until a signal is received. This is not the same as calling sigrelse followed by pause; sigpause is an atomic operation that cannot be interrupted in between the change in the signal mask and the suspension of the process.
We can use these functions to fix the second problem described in the previous section:
    void handler(int);
    int flag = 0;
    
    int
    main(void)
    {
        ...
        sighold(SIGALRM);
        sigset(SIGALRM, handler);
    
        while (flag == 0)
            sigpause(SIGALRM);
        ...
    }
    
    void
    handler(sig)
    {
        flag = 1;
    }
The initial call to sighold adds the alarm signal to the process' signal mask; this means the signal can only be delivered when the process is ready for it. The call to sigpause removes the alarm signal from the signal mask and suspends the program. Because the signal is normally blocked, it is not possible for it to arrive after the test of flag and before the call to sigpause.
Example 10-3 shows a reimplementation of our signal program using reliable signals.
Example 10-3:  signal3
#include <signal.h>
#include <stdio.h>
void handler(int);
int
main(void)
{
    /*
     * Send SIGUSR1 and SIGUSR2 to the handler function.
     */
    if (sigset(SIGUSR1, handler) == SIG_ERR) {
        fprintf(stderr, "cannot set handler for SIGUSR1\n");
        exit(1);
    }
    if (sigset(SIGUSR2, handler) == SIG_ERR) {
        fprintf(stderr, "cannot set handler for SIGUSR2\n");
        exit(1);
    }
    /*
     * Now wait for signals to arrive.
     */
    for (;;)
        pause();
}
/*
* handler - handle a signal.
*/
void
handler(int sig)
{
    /*
     * Print out what we received.
     */
    psignal(sig, "Received signal");
}
    % signal3 &
    [1] 12345
    % kill -USR1 12345
    Received signal: Signal User 1
    % kill -USR2 12345
    Received signal: Signal User 2
    % kill -USR1 12345
    Received signal: Signal User 1
    % kill -USR2 12345
    Received signal: Signal User 2
    % kill 12345
    [1] + Terminated     signal3

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback