Inter-Process Communication in Operating Systems: Signal Mechanism
Description:
Signals are an asynchronous inter-process communication mechanism in operating systems, used to notify a target process that a specific event has occurred. Similar to hardware interrupts, signals are software-level "interrupts" used to handle exceptions, user interactions, process control, and other scenarios.
Basic Concepts of Signals:
- A signal is essentially an integer identifier, each corresponding to a specific event (e.g., SIGINT=2 indicates keyboard interrupt).
- After a signal is sent, it may be ignored, caught, or trigger a default action.
- Signal handling is asynchronous; a process may be interrupted by a signal at any time.
Ways Signals Are Generated:
- Keyboard input: Ctrl+C generates SIGINT, Ctrl+\ generates SIGQUIT.
- Hardware exceptions: Division by zero error generates SIGFPE, illegal memory access generates SIGSEGV.
- System calls: The kill() function allows a process to send signals to other processes.
- Software conditions: Broken pipe generates SIGPIPE, timer expiration generates SIGALRM.
Signal Handling Process:
Step 1: Signal Sending
- The kernel detects a signal event (e.g., keyboard interrupt).
- Sets the corresponding bit in the signal bitmap of the target process's Process Control Block (PCB).
- If the process is in an interruptible sleep state, wakes it up.
Step 2: Signal Delivery Timing
- Before a process returns from kernel mode to user mode (after system calls or interrupt handling).
- When a process is awakened from a sleep state.
- During context switching when a process's time slice expires.
Step 3: Signal Handling Methods
- Default action: Terminate the process, ignore the signal, terminate and generate a core dump, etc.
- Ignore the signal: Explicitly instruct the kernel to discard the signal.
- Catch the signal: Register a signal handler function, which is automatically called when the signal occurs.
Example of Signal Handler Registration:
#include <signal.h>
void signal_handler(int sig) {
// Signal handling logic
}
int main() {
// Register the signal handler for SIGINT
signal(SIGINT, signal_handler);
while(1) {
// Main program loop
}
return 0;
}
Important Characteristics of Signal Handling:
Unreliable Signals Problem:
- Early UNIX signals could be lost (multiple occurrences of the same signal were recorded only once).
- During the execution of a signal handler, the same signal type might be automatically blocked.
- Modern systems have resolved this issue with real-time signals.
Signal Mask:
- Each process has a signal mask defining the set of currently blocked signals.
- Blocked signals are delayed until they are unblocked.
- Use the sigprocmask() function to set the signal mask.
Reentrant Functions:
- Signal handlers must use reentrant functions (e.g., write()).
- Avoid non-reentrant functions (e.g., malloc, printf).
- Because signals can interrupt the main program execution at any moment.
Practical Application Scenarios:
- Graceful shutdown: Catch SIGTERM to clean up resources.
- Child process management: Parent process catches SIGCHLD to avoid zombie processes.
- Timeout control: Use SIGALRM to implement operation timeouts.
- Debugging support: Use SIGTRAP for program debugging.
Limitations of the Signal Mechanism:
- Limited information (only the signal identifier can be passed).
- Priority issues (all signals are equal, no priority distinction).
- Real-time performance is inferior to hardware interrupts.
- Complex signal handling may introduce race conditions.
By understanding the signal mechanism, you can better handle process exceptions, implement inter-process coordination, and write more robust system programs.