Inter-Process Communication in Operating Systems: Signal Mechanism

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:

  1. A signal is essentially an integer identifier, each corresponding to a specific event (e.g., SIGINT=2 indicates keyboard interrupt).
  2. After a signal is sent, it may be ignored, caught, or trigger a default action.
  3. Signal handling is asynchronous; a process may be interrupted by a signal at any time.

Ways Signals Are Generated:

  1. Keyboard input: Ctrl+C generates SIGINT, Ctrl+\ generates SIGQUIT.
  2. Hardware exceptions: Division by zero error generates SIGFPE, illegal memory access generates SIGSEGV.
  3. System calls: The kill() function allows a process to send signals to other processes.
  4. 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

  1. Default action: Terminate the process, ignore the signal, terminate and generate a core dump, etc.
  2. Ignore the signal: Explicitly instruct the kernel to discard the signal.
  3. 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:

  1. Graceful shutdown: Catch SIGTERM to clean up resources.
  2. Child process management: Parent process catches SIGCHLD to avoid zombie processes.
  3. Timeout control: Use SIGALRM to implement operation timeouts.
  4. 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.