Signals

CS 321 Lecture, Dr. Lawlor, 2006/01/27

The Silberschatz book documents user/supervisor mode in section 1.5.1, and signals in section 4.4.3, and 21.9.1.

Interrupts as the security access gateway

(We actually covered this material in lecture on Wednesday)

The CPU itself actually has a simple but powerful security mechanism built in.  This mechanism is just a single bit, variously called the "supervisor bit" (colloquially), "mode bit" (in the Silberschatz book), or "I/O Privilege Level" (on x86).  If the bit is 0, you're in "supervisor mode" (or "kernel mode"), and that means you're the OS--you can do anything.  Specifically, if you try to access the I/O hardware, memory management unit, or any other "protected" resource, the CPU itself waves you through.

If the bit is 1, you're a stinkin' user, and you can't get away with nuttin'.  ("peon bit" would be a better name.)  If you try to access any protected resource, the CPU itself issues an interrupt, which is handled by the OS.  The OS can then check your access rights, and if you're OK, the OS can allow the access.  If you're not allowed access, the OS can then kill your program, optionally emailing the Microsoft lawyers that a rogue program just tried to take the system down.

You can always switch from supervisor mode to user mode--that is, give up your OS rights and become a regular user.  But it's intentionally very hard to switch back from user mode to supervisor mode.  In fact, if you're in user mode, on most machines there are only two ways to enter supervisor mode:

Signals: Interrupts for ordinary processes

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.
  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:
(Executable NetRun Link)
#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;
}

Which on my machine prints out:

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

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

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).

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