There are a number of ways in which an external event can be supplied to a process. One approach is to make the process query the kernel explicitly to determine if the event had taken place (essentially polling for the event).
Another approach, supported by most operating systems, is for the kernel (or a process) to call a subroutine specified by a running process when an event occurs. The event, usually referred to as a signal, is thrown to the subroutine, sometimes referred to as a catcher.
Signals can be looked upon as interrupts which are handled by a process. The process is interrupted and control passes to the catcher, which performs a set of instructions associated with the signal. When the catcher has completed its task, control returns to the process.
Most operating systems support a wide range of signals, some are used for interprocess communication, while others handle software faults that occur within the running process.
Since most applications (running as processes) have little interest in handling signals, all signals are associated with default actions (often terminating the running process). Processes can also choose to ignore signals (there are usually restrictions placed upon this, since fatal actions, such as memory violation, usually indicate that there is something wrong with the application).
There are a wide range of signals (most versions of Unix support thirty or more); some common signals include:
Signal | Meaning |
---|---|
SIGBRK | Break detected from keyboard |
SIGABRT | Abnormal termination |
SIGFPE | Floating point exception |
SIGILL | Illegal opcode |
SIGINT | Control-C interrupt detected from keyboard |
SIGSEGV | Illegal memory access |
SIGTERM | Request for program termination |
Note in keeping with the Unix approach of capitalizing defined constants, all signals are in capital letters.
There are a number of functions available for signal handling, some are listed here.
The function signal() allows a process to indicate what action is to take place when a specific signal occurs. The signal is an operating system specific signal (for example, see above). The action is the name of a function which is to be called if the signal occurs. For example, to catch SIGABRT, one could write the following:
void catch_abort() { /* Code to handle SIGABRT */ } main() { ... signal(SIGABRT, catch_abort); ... }
Some applications may choose to explicitly ignore a signal, while others may require the action to revert back to the system default action. There are two 'functions' available for this action:
Function | Action |
---|---|
SIG_DFL | Peforms the default action associated with the signal |
SIG_IGN | Ignores the signal should it occur |
For example, to ignore any system-defined actions for SIGABRT, one could write:
main() { ... signal(SIGABRT, SIG_IGN); ... }
If the SIGABRT signal should occur, it is ignored by this process.
A process can throw a signal to itself using the raise() function. The signal specified will result in whatever action the application has requested for the signal.
Raise() can be used in various situations, including the testing of catchers or indicating to a process that a specific set of instructions have been executed (i.e., debugging).
The kill() function allows one process to send a signal to another. Kill() might seem like a misnomer, but in most early versions of Unix, this function was associated with SIGTERM (terminate a specific process).
© 2003 - Whale Lake Press