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:
- Take an interrupt. The CPU always sets the supervisor bit at the
start of an interrupt handler. This means you've got to make sure user
processes can't mess up the interrupt table (which lists the interrupt
routines)!
- Reboot. The machine will boot back up in supervisor mode. (The
286 actually used this approach--rebooting was the only way to exit
32-bit mode and get back to 16-bit 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:
- 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.
- The CPU looks up the OS interrupt service routine in the interrupt table.
- 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.
- 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:
- SIGSEGV, segmentation fault, is delivered when your
program accesses an out-of-bounds memory address. If you manipulate the
memory map, you can actually resume from this signal.
- SIGFPE, floating-point (or arithmetic) exception, is
delivered when you divide by zero or encounter a problem with
floating-point (like a signalling NaN).
- SIGILL, illegal instruction, is delivered when your
program hits an invalid instruction, usually caused by overwriting your
own code or jumping to a bad function pointer.
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.