Signals are a way of sending simple messages to processes. Most of these messages are already defined and can be found in <linux/signal.h>. However, signals can only be processed when the process is in user mode. If a signal has been sent to a process that is in kernel mode, it is dealt with immediately on returning to user mode.
Signals are one of the oldest inter-process communication methods used by Unix TM systems. They are used to signal asynchronous events to one or more processes. A signal could be generated by a keyboard interrupt or an error condition such as the process attempting to access a non-existent location in its virtual memory. Signals are also used by the shells to signal job control commands to their child processes.
There are a set of defined signals that the kernel can generate or that can be generated by other processes in the system, provided that they have the correct privileges. You can list a system’s set of signals using the kill command (kill -l), on my Intel Linux box this gives:
|1) SIGHUP||2) SIGINT||3) SIGQUIT||4) SIGILL|
|5) SIGTRA||6) SIGIOT||7) SIGBUS||8) SIGFPE|
|9) SIGKILL||10) SIGUSR1||11) SIGSEGV||12) SIGUSR2|
|13) SIGPIPE||14) SIGALRM||15) SIGTERM||17) SIGCHLD|
|18) SIGCONT||19) SIGSTOP||20) SIGTSTP||21) SIGTTIN|
|22) SIGTTOU||23) SIGURG||24) SIGXCPU||25) SIGXFSZ|
|26) SIGVTALRM||27) SIGPROF||28) SIGWINCH||29) SIGIO|
The numbers are different for an Alpha AXP Linux box. Processes can choose to ignore most of the signals that are generated, with two notable exceptions: neither the SIGSTOP signal which causes a process to halt its execution nor the SIGKILL signal which causes a process to exit can be ignored. Otherwise though, a process can choose just how it wants to handle the various signals. Processes can block the signals and, if they do not block them, processes can either choose to handle the signals themselves or allow the kernel to handle them. If the kernel handles the signals, it will do the default actions required for this signal. For example, the default action when a process receives the SIGFPE (floating point exception) signal is to core dump and then exit. Signals have no inherent relative priorities. If two signals are generated for a process at the same time then they may be presented to the process or handled in any order. Also there is no mechanism for handling multiple signals of the same kind. There is no way that a process can tell if it received 1 or 42 SIGCONT signals.
Linux implements signals using information stored in the task_struct for the process. The number of supported signals is limited to the word size of the processor. Processes with a word size of 32 bits can have 32 signals whereas 64 bit processors like the Alpha AXP may have up to 64 signals. The currently pending signals are kept in the signal field with a mask of blocked signals held in blocked. With the exception of SIGSTOP and SIGKILL, all signals can be blocked. If a blocked signal is generated, it remains pending until it is unblocked. Linux also holds information about how each process handles every possible signal and this is held in an array of sigaction data structures pointed at by the task_struct for each process. Amongst other things it contains either the address of a routine that will handle the signal or a flag which tells Linux that the process either wishes to ignore this signal or let the kernel handle the signal for it. The process modifies the default signal handling by making system calls and these calls alter the sigaction for the appropriate signal as well as the blocked mask.
Not every process in the system can send signals to every other process. For security reason (among other things) only the kernel can and super users can send signals to all processes. Normal processes can only send signals to processes with the same uid and gid or to processes in the same process group. Signals are generated by setting the appropriate bit in the task_struct‘s signal field. If the process has not blocked the signal and is waiting but interruptible (in state Interruptible) then it is woken up by changing its state to Running and making sure that it is in the run queue. That way the scheduler will consider it a candidate for running when the system next schedules. If the default handling is needed, then Linux can optimize the handling of the signal. For example if the signal SIGWINCH (the X window changed focus) and the default handler is being used then there is nothing to be done.
Signals are not presented to the process immediately after they are generated. Instead, they must wait until the process is running again. Every time a process exits from a system call, its signal and blocked fields are checked and, if there are any unblocked signals, they can now be delivered. This might seem a very unreliable method since it is dependant on the processes checking the signals, but every process in the system is making system calls, for example to write a character to the terminal, all of the time. Processes can elect to wait for signals if they wish, they are suspended in state Interruptible until a signal is presented. The Linux signal processing code looks at the sigaction structure for each of the current unblocked signals.
If a signal’s handler is set to the default action, then the kernel will handle it. The SIGSTOP signal’s default handler will change the current process’ state to Stopped, then run the scheduler to select a new process to run. The default action for the SIGFPE signal core dumps the process and then causes it to exit. Alternatively, a process may have specfied its own signal handler. This is a routine that is called whenever the signal is generated and the sigaction structure holds the routine’s address. It is the kernel’s job to call the process’ signal handling routine. How this happens is processor specific but all CPUs must cope with the fact that the current process that has been running in kernel mode is just about to return to the calling process in user mode. The problem is solved by doing several things such as manipulating the process’ stack and registers, resetting the process’ program counter to the address of its signal handling routine and either adding the parameters of the routine to the call frame or passing in registers. Whatever the CPU’s exact mechanism, when the process resumes operation it appears as if the signal handling routine had been called normally.
Linux is POSIX compatible and so the process can specify which signals are blocked when a particular signal handling routine is called. This entails changing the blocked mask during the call to the processes signal handler. The blocked mask must be returned to its original value when the signal handling routine has finished. Therefore Linux adds a call to a tidy up routine that will restore the original blocked mask onto the call stack of the signalled process. Linux also optimizes the case where several signal handling routines need to be called by stacking them so that each time one handling routine exits, the next one is called until the tidy up routine is called.
Many signals (such as signal 9, SIGKILL), have the ability to immediately terminate a process. However, most of these signals can be either ignored or dealt with by the process itself. If not, the kernel will take the default action specified for that signal. You can send signals to processes yourself by means of the kill command, as well as by the Delete key and Ctrl+/. However, you can only send signals to processes that you own unless you are root. If you are root, you can send signals to any process.
It’s possible that the process to which you want to send the signal is sleeping. If that process is sleeping at an interruptible priority, then the process will awaken to handle the signal.
The kernel keeps track of pending signals in each process’ process structure. This is a 32-bit value in which each bit represents a single signal. Because it is only one bit per signal, there can only be one signal pending of each type. If there are different kinds of signals pending, the kernel has no way of determining which came in when. It will therefore process the signals starting at the lowest numbered signal and moving up.