home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


16.17. Writing a Signal Handler

Problem

You want to write a subroutine that will be called whenever your program receives a signal.

Solution

A signal handler is just a subroutine. With some risk, you can do anything in a signal handler you'd do in any Perl subroutine, but the more you do, the riskier it gets.

Some systems require you to reinstall your signal handler after each signal:

$SIG{INT} = \&got_int;
sub got_int {
    $SIG{INT} = \&got_int;          # but not for SIGCHLD!
    # ...
}

Some systems restart blocking operations, such as reading data. In such cases, you must call die within the handler and trap it with eval :

my $interrupted = 0;

sub got_int {
    $interrupted = 1;
    $SIG{INT} = 'DEFAULT';          # or 'IGNORE'
    die;
}

eval {
    $SIG{INT} = \&got_int;
    # ... long-running code that you don't want to restart
};

if ($interrupted) {
    # deal with the signal
}

Discussion

Installing a custom signal handling subroutine is a lot like playing with fire. It may seem like a lot of fun, but, sooner or later, you're going to get burned unless you're exceedingly careful. By installing Perl code to deal with signals, you're exposing yourself to two dangers. First, few system library functions are re-entrant. If the signal interrupts while Perl is executing one function (like malloc (3) or printf (3)), and your signal handler then calls the same function again, you could get unpredictable behavior - often, a core dump. Second, Perl isn't itself re-entrant at the lowest levels. (Release 5.005 of Perl supports lightweight processes called threads .) If the signal interrupts Perl while Perl is changing its own internal data structures, unpredictable behavior may result - usually random core dumps.

You have two options: be paranoid or be pragmatic. The paranoid approach is to do as little as possible in your signal handler, as exemplified by the eval and die code in the Solution - set a variable that already has a value, and then bail. Even this is cavalier for the true paranoiac, who avoids die in a handler because the system is out to get you. The pragmatic approach is to say "I know the risks, but prefer the convenience," and to do anything you want in your signal handler.

Signals have been implemented in many different operating systems, often in slightly different flavors. The two situations where signal implementations vary the most are when a signal occurs when its signal handler is active ( reliability ), and when a signal interrupts a blocking system call like read or accept ( restarting ).

The initial implementation of signals was unreliable, meaning that while a handler was running, further occurrences of the same signal would cause the default action, likely aborting the program. Later systems addressed this (each in their own subtly different way, of course) by providing a way to block the delivery of further signals of that number until the handler has finished. If Perl detects that your system can use reliable signals, it generates the proper system calls to achieve this saner, safer behavior. You can use POSIX signals to block signal delivery at other times, as described in Recipe 16.20 .

For truly portable code, the paranoid programmer will assume the worst case (unreliable signals) and reinstall the signal handler manually, usually as the first statement in a function:

$SIG{INT} = \&catcher;
sub catcher {
    $SIG{INT} = \&catcher;
    # ...
}

In the special case of catching SIGCHLD, see Recipe 16.19 . System V has bizarre behavior that can trip you up.

Use the Config module to find out whether you have reliable signals:

use Config;
print "Hurrah!\n" if $Config{d_sigaction};

Just because you have reliable signals doesn't mean you automatically get reliable programs. But without them, you certainly won't.

The first implementation of signals interrupted slow system calls, functions that require the cooperation of other processes or device drivers. If a signal comes in while those system calls are still running, they (and their Perl counterparts) return an error value and set the error to EINTR, "Interrupted system call" . Checking for this condition made programs so complicated that most didn't check, and therefore misbehaved or died if a signal interrupted a slow system call. Most modern versions of Unix allow you to change this behavior. Perl will always make system calls restartable if it is on a system that support it. If you have a POSIX system, you can control restarting using the POSIX module (see Recipe 16.20 ).

To determine whether your interrupted system calls will automatically restart, look at your system's C signal.h include file:

% egrep 'S[AV]_(RESTART|INTERRUPT)' /usr/include/*/signal.h

Two signals are untrappable and unignorable: SIGKILL and SIGSTOP. Full details of the signals available on your system and what they mean can be found in the signal (3) manpage.

See Also

The "Signals" sections in Chapter 6 of Programming Perl and in perlipc (1); your system's sigaction (2), signal (3), and kill (2) manpages (if you have them). Porting UNIX Software , by Greg Lehey, O'Reilly & Associates, (1995); Advanced Programming in the Unix Environment