Catching Signals

CS 301: Assembly Language Programming Lecture, Dr. Lawlor

    "Guido here is going to send you a signal.  He's going to set your car on fire."
        -- hypothetical OS Mafia don

A signal is when the OS calls you:

A signal is usually not something you can ignore--the default signal handler usually exits your process.

Signals are available on all POSIX operating systems (including Windows, Linux, Mac OS X), and include many program errors:

Signals can also be used to indicate that I/O is ready (SIGIO, enabled using ``fcntl''), that a timer has expired (SIGALRM, SIGPROF, or SIGVPROF, enabled using ``setitimer''), that the operating system wants you to shut down (SIGTERM, SIGQUIT, SIGKILL, all UNIX-specific), that various events have happened on the terminal (SIGHUP, SIGWINCH, SIGPIPE, SIGTTIN, SIGTTOU, all UNIX-specific), or for application-defined purposes (SIGUSR1/SIGUSR2, which must be sent explicitly).  See signal.h for the full list of signals.

Signals, exactly like interrupts, are hence a generic ``catch-all'' notification mechanism, used for a variety of unrelated tasks.

Writing a Signal Handler

Signals can be seen as a standardized interface for delivering interrupts to user programs. Exactly like interrupts, a signal handler is just a subroutine that gets called when something weird happens.

Overall signal delivery looks like this:

  1. Something causes an interrupt--a hardware device needs attention, or a program reads a bad memory address, divides by zero, executes an illegal or privileged instruction, etc.
  2. The CPU looks up the OS interrupt service routine in the interrupt table (or "interrupt vector", for some strange reason.)
  3. The OS's interrupt service routine figures out if it can handle the interrupt, or if it should deliver the interrupt to a process as a signal.
  4. To deliver a signal, the OS essentially just calls your process's subroutine.

To set yourself up to receive signals (add a signal handler), you just call an operating system routine like signal. You pass in the name of the signal you want to receive, and a function to execute once the signal is received.  For example:

#include <signal.h>

void myHandler(int i)
{
printf("Sorry dude--you just hit signal %d\n",i);
exit(1);
}

int foo(void) {
int *badPointer=(int *)0;
printf("Installing signal handler\n");
signal(SIGSEGV,myHandler); /* <------------- */
printf("Signal handler installed. Segfaulting...\n");
(*badPointer)++;
printf("Back from segfault?!\n");
return 0;
}
(Executable NetRun Link)

Which on my machine prints out:

Installing signal handler
Signal handler installed. Segfaulting...
Sorry dude--you just hit signal 11

Typically you do *not* want to just return from a signal handler without actually handling the problem.