- 1 The Role of Signals
- 2 Generating a Signal
- 3 Delivering a Signal
- 4 System Calls Related to Signal Handling
A signal is a very short message that may be sent to a process or a group of processes. The only information given to the process is usually a number identifying the signal.
A set of macros whose names start with the prefix
SIG is used to identify signals.
Signals serve two main purposes:
- To make a process aware that a specific event has occurred
- To cause a process to execute a signal handler function included in its code
Besides the regular signals described in this table, the POSIX standard has introduced a new class of signals denoted as real-time signals; their signal numbers range from 32 to 64 on Linux. They mainly differ from regular signals because they are always queued so that multiple signals sent will be received. On the other hand, regular signals of the same kind are not queued: if a regular signal is sent many times in a row, just one of them is delivered to the receiving process. Although the Linux kernel does not use real-time signals, it fully supports the POSIX standard by means of several specific system calls.
A number of system calls allow programmers to send signals and determine how their processes respond to the signals they receive.
An important characteristic of signals is that they may be sent at any time to a process whose state is usually unpredictable. Signals sent to a process that is not currently executing must be saved by the kernel until that process resumes execution. Blocking a signal requires that delivery of the signal be held off until it is later unblocked.
Therefore, the kernel distinguishes two different phases related to signal transmission:
- Signal generation
The kernel updates a data structure of the destination process to represent that a new signal has been sent.
- Signal delivery
The kernel forces the destination process to react to the signal by changing its execution state, by starting the execution of a specified signal handler, or both.
Signals that have been generated but not yet delivered are called pending signals. At any time, only one pending signal of a given type may exist for a process; additional pending signals of the same type to the same process are not queued but simply discarded. Real-time signals are different, though: there can be several pending signals of the same type.
There are three ways in which a process can respond to a signal:
- Explicitly ignore the signal.
- Execute the default action associated with the signal.
- Catch the signal by invoking a corresponding signal-handler function.
SIGSTOP signals cannot be ignored, caught, or blocked, and their default actions must always be executed.
Furthermore, a pending signal is private if it has been sent to a specific process; it is shared if it has been sent to a whole thread group.
For each process in the system, the kernel must keep track of what signals are currently pending or masked; the kernel must also keep track of how every thread group is supposed to handle every signal.
The signal descriptor and the signal handler descriptor
signal field of the process descriptor points to a signal descriptor, a
signal_struct structure that keeps track of the shared pending signals. The signal descriptor is shared by all processes belonging to the same thread group.
Besides the signal descriptor, every process refers also to a signal handler descriptor, which is a
sighand_struct structure describing how each signal must be handled by the thread group.
The sigaction data structure
The pending signal queues
Many kernel functions generate signals: they accomplish the first phase of signal handling——by updating one or more process descriptors as needed. They do not directly perform the second phase of delivering the signal but, depending on the type of signal and the state of the destination processes, may wake up some processes and force them to receive the signal.
- When a signal is sent to a process, either from the kernel or from another process.
- When a signal is sent to a whole thread group, either from the kernel or from another process.
We assume that the kernel noticed the arrival of a signal and prepareD the process descriptor of the process that is supposed to receive the signal. But in case that process was not running on the CPU at that moment, the kernel deferred the task of delivering the signal.
The kernel checks for the existence of pending signals every time it finishes handling an interrupt or an exception.
Linux kernel每隔固定周期会发出timer interrupt。
The request associated with a system call cannot always be immediately satisfied by the kernel; when this happens, the process that issued the system call is put in a
If the process is put in a
TASK_INTERRUPTIBLE state and some other process sends a signal to it, the kernel puts it in the
TASK_RUNNING state without completing the system call.The signal is delivered to the process while switching back to User Mode. When this happens, the system call service routine does not complete its job, but returns an
ERESTARTNOINTR error code.
In practice, the only error code a User Mode process can get in this situation is
EINTR, which means that the system call has not been completed.The remaining error codes are used internally by the kernel to specify whether the system call may be reexecuted automatically after the signal handler termination.
Restarting a system call interrupted by a non-caught signal
Restarting a system call for a caught signal