Signal Module Introduction and Concepts

🏷️ Operating System and System Operations / Working with Processes and Signals

When you're working with long-running scripts or system-level tools, you'll often need to handle interruptions, clean up resources, or communicate with running processes. Python's signal module gives you the ability to catch and respond to operating system signalsβ€”like when someone presses Ctrl+C or when a process needs to shut down gracefully. This module is essential for writing robust scripts that behave well in production environments.


βš™οΈ What Are Signals?

Signals are asynchronous notifications sent to a process by the operating system (or by another process). They are a form of inter-process communication used to indicate events like:

  • SIGINT – Interrupt from keyboard (usually Ctrl+C)
  • SIGTERM – Termination request (common in system shutdowns)
  • SIGHUP – Hangup detected (often used to reload configuration)
  • SIGKILL – Force kill (cannot be caught or ignored)
  • SIGUSR1 / SIGUSR2 – User-defined signals for custom behavior

Each signal has a default behavior (like terminating the process), but you can override that behavior using Python's signal module.


πŸ› οΈ Why Use the Signal Module?

  • Graceful shutdown – Clean up files, close database connections, or save state before exiting.
  • Custom behavior – Reload configuration files without restarting the script.
  • Timeout handling – Set alarms to interrupt long-running operations.
  • Process coordination – Send signals between parent and child processes.

πŸ•΅οΈ Core Concepts

  • Signal Handlers – Functions you define to run when a specific signal is received.
  • Default Actions – What the OS does if you don't set a custom handler (e.g., terminate, ignore, or stop).
  • Blocking Signals – Temporarily preventing a signal from being delivered.
  • Signal Numbers – Each signal has a numeric identifier (e.g., SIGINT is signal 2, SIGTERM is signal 15).

πŸ“Š Common Signals and Their Meanings

Signal Name Number Default Action Typical Use Case
SIGINT 2 Terminate Interrupt from keyboard (Ctrl+C)
SIGTERM 15 Terminate Graceful termination request
SIGHUP 1 Terminate Reload configuration or restart
SIGUSR1 10 Terminate Custom user-defined action
SIGUSR2 12 Terminate Custom user-defined action
SIGALRM 14 Terminate Timer expiration (used with signal.alarm())

πŸ§ͺ Basic Usage Pattern

The typical workflow for using the signal module follows these steps:

  1. Define a handler function – This function will be called when the signal is received. It usually takes two arguments: the signal number and the current stack frame.
  2. Register the handler – Use signal.signal(signal_number, handler_function) to tell Python which function to call for a given signal.
  3. Let the program run – The handler will be invoked automatically when the signal arrives.
  4. Use signal.alarm() (optional) – Set a timer that sends SIGALRM after a specified number of seconds.

🧩 Simple Example (No Code Blocks)

To catch Ctrl+C and print a message instead of immediately exiting, you would:

  • Define a function called handle_interrupt that prints "Shutting down gracefully..." and then calls sys.exit(0).
  • Register this function with signal.signal(signal.SIGINT, handle_interrupt).
  • Run your main program loop. When the user presses Ctrl+C, your handler runs instead of the default termination.

For a timeout scenario:

  • Set an alarm using signal.alarm(5) to trigger after 5 seconds.
  • Define a handler for SIGALRM that prints "Operation timed out" and raises an exception.
  • Run your long operation. If it finishes before 5 seconds, call signal.alarm(0) to cancel the timer.

⚠️ Important Considerations

  • SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. They are reserved for the operating system.
  • Signal handlers must be simple – Avoid complex logic or blocking calls inside handlers. They run asynchronously and can cause race conditions.
  • Windows limitations – The signal module works on Windows, but only a subset of signals is available (e.g., SIGINT, SIGTERM). Unix-specific signals like SIGHUP or SIGUSR1 are not supported.
  • Threading conflicts – Signals are only handled in the main thread. If you use threads, signals will be delivered to the main thread only.
  • Restoring defaults – You can reset a signal to its default behavior by passing signal.SIG_DFL as the handler.

🧠 Best Practices for Engineers

  • Always define a handler for SIGINT and SIGTERM in long-running scripts to ensure clean shutdown.
  • Use signal.alarm() to implement timeouts for operations that might hang indefinitely.
  • Keep handler functions short – set a flag or queue an event, then handle the actual work in the main loop.
  • Test your signal handling in both interactive and daemon (background) modes.
  • Remember that signals are delivered asynchronously – your program could be in the middle of anything when a signal arrives.

πŸ“¦ Summary

  • The signal module lets you intercept OS-level signals and define custom behavior.
  • Common signals include SIGINT (Ctrl+C), SIGTERM (termination), and SIGALRM (timer).
  • Handlers are simple functions registered with signal.signal().
  • Use signals for graceful shutdowns, timeouts, and configuration reloads.
  • Be mindful of platform limitations and keep handlers lightweight.

Mastering the signal module gives you fine-grained control over how your Python scripts respond to system eventsβ€”a critical skill for writing production-ready tools and services.


The Python signal module lets engineers handle operating system signals β€” asynchronous notifications sent to a running process.

πŸ›‘ Example 1: Listing available signals on your system

This example shows how to see all signal names defined in the module.

import signal

available_signals = dir(signal)
print(available_signals[:10])

πŸ“€ Output: ['SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 'SIGILL', 'SIGINT']


⏰ Example 2: Setting an alarm signal with SIGALRM

This example demonstrates how to schedule a signal to be sent after a fixed number of seconds.

import signal
import time

def alarm_handler(signum, frame):
    print("Alarm went off!")

signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(2)

print("Waiting for alarm...")
time.sleep(3)

πŸ“€ Output: Waiting for alarm...
πŸ“€ Output: Alarm went off!


⌨️ Example 3: Ignoring the interrupt signal (SIGINT)

This example shows how to make a process ignore Ctrl+C keyboard interrupts.

import signal
import time

def ignore_interrupt(signum, frame):
    print("Interrupt received, but ignoring it")

signal.signal(signal.SIGINT, ignore_interrupt)

print("Press Ctrl+C β€” I will ignore it")
time.sleep(5)
print("Done β€” still running")

πŸ“€ Output: Press Ctrl+C β€” I will ignore it
πŸ“€ Output: (if you press Ctrl+C) Interrupt received, but ignoring it
πŸ“€ Output: Done β€” still running


⏱️ Example 4: Using SIGALRM to timeout a long operation

This example demonstrates a practical use of signals to enforce a time limit on code execution.

import signal

def timeout_handler(signum, frame):
    raise TimeoutError("Operation took too long")

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(3)

try:
    print("Starting slow operation...")
    import time
    time.sleep(5)
    print("Operation completed")
except TimeoutError as e:
    print(f"Timed out: {e}")
finally:
    signal.alarm(0)

πŸ“€ Output: Starting slow operation...
πŸ“€ Output: Timed out: Operation took too long


πŸ”„ Example 5: Handling SIGCHLD to detect child process exit

This example shows how to catch when a child process finishes.

import signal
import os
import time

def child_done(signum, frame):
    print("Child process has exited")

signal.signal(signal.SIGCHLD, child_done)

pid = os.fork()
if pid == 0:
    print("Child process running")
    time.sleep(1)
    os._exit(0)
else:
    print("Parent waiting for child")
    time.sleep(2)
    print("Parent done")

πŸ“€ Output: Parent waiting for child
πŸ“€ Output: Child process running
πŸ“€ Output: Child process has exited
πŸ“€ Output: Parent done


Comparison Table

Signal Name Purpose Typical Use Case
SIGALRM Timer expiration Timeout enforcement
SIGINT Keyboard interrupt Graceful shutdown
SIGCHLD Child process status change Process monitoring
SIGTERM Termination request Clean program exit
SIGKILL Force kill (cannot be caught) Emergency stop

When you're working with long-running scripts or system-level tools, you'll often need to handle interruptions, clean up resources, or communicate with running processes. Python's signal module gives you the ability to catch and respond to operating system signalsβ€”like when someone presses Ctrl+C or when a process needs to shut down gracefully. This module is essential for writing robust scripts that behave well in production environments.


βš™οΈ What Are Signals?

Signals are asynchronous notifications sent to a process by the operating system (or by another process). They are a form of inter-process communication used to indicate events like:

  • SIGINT – Interrupt from keyboard (usually Ctrl+C)
  • SIGTERM – Termination request (common in system shutdowns)
  • SIGHUP – Hangup detected (often used to reload configuration)
  • SIGKILL – Force kill (cannot be caught or ignored)
  • SIGUSR1 / SIGUSR2 – User-defined signals for custom behavior

Each signal has a default behavior (like terminating the process), but you can override that behavior using Python's signal module.


πŸ› οΈ Why Use the Signal Module?

  • Graceful shutdown – Clean up files, close database connections, or save state before exiting.
  • Custom behavior – Reload configuration files without restarting the script.
  • Timeout handling – Set alarms to interrupt long-running operations.
  • Process coordination – Send signals between parent and child processes.

πŸ•΅οΈ Core Concepts

  • Signal Handlers – Functions you define to run when a specific signal is received.
  • Default Actions – What the OS does if you don't set a custom handler (e.g., terminate, ignore, or stop).
  • Blocking Signals – Temporarily preventing a signal from being delivered.
  • Signal Numbers – Each signal has a numeric identifier (e.g., SIGINT is signal 2, SIGTERM is signal 15).

πŸ“Š Common Signals and Their Meanings

Signal Name Number Default Action Typical Use Case
SIGINT 2 Terminate Interrupt from keyboard (Ctrl+C)
SIGTERM 15 Terminate Graceful termination request
SIGHUP 1 Terminate Reload configuration or restart
SIGUSR1 10 Terminate Custom user-defined action
SIGUSR2 12 Terminate Custom user-defined action
SIGALRM 14 Terminate Timer expiration (used with signal.alarm())

πŸ§ͺ Basic Usage Pattern

The typical workflow for using the signal module follows these steps:

  1. Define a handler function – This function will be called when the signal is received. It usually takes two arguments: the signal number and the current stack frame.
  2. Register the handler – Use signal.signal(signal_number, handler_function) to tell Python which function to call for a given signal.
  3. Let the program run – The handler will be invoked automatically when the signal arrives.
  4. Use signal.alarm() (optional) – Set a timer that sends SIGALRM after a specified number of seconds.

🧩 Simple Example (No Code Blocks)

To catch Ctrl+C and print a message instead of immediately exiting, you would:

  • Define a function called handle_interrupt that prints "Shutting down gracefully..." and then calls sys.exit(0).
  • Register this function with signal.signal(signal.SIGINT, handle_interrupt).
  • Run your main program loop. When the user presses Ctrl+C, your handler runs instead of the default termination.

For a timeout scenario:

  • Set an alarm using signal.alarm(5) to trigger after 5 seconds.
  • Define a handler for SIGALRM that prints "Operation timed out" and raises an exception.
  • Run your long operation. If it finishes before 5 seconds, call signal.alarm(0) to cancel the timer.

⚠️ Important Considerations

  • SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. They are reserved for the operating system.
  • Signal handlers must be simple – Avoid complex logic or blocking calls inside handlers. They run asynchronously and can cause race conditions.
  • Windows limitations – The signal module works on Windows, but only a subset of signals is available (e.g., SIGINT, SIGTERM). Unix-specific signals like SIGHUP or SIGUSR1 are not supported.
  • Threading conflicts – Signals are only handled in the main thread. If you use threads, signals will be delivered to the main thread only.
  • Restoring defaults – You can reset a signal to its default behavior by passing signal.SIG_DFL as the handler.

🧠 Best Practices for Engineers

  • Always define a handler for SIGINT and SIGTERM in long-running scripts to ensure clean shutdown.
  • Use signal.alarm() to implement timeouts for operations that might hang indefinitely.
  • Keep handler functions short – set a flag or queue an event, then handle the actual work in the main loop.
  • Test your signal handling in both interactive and daemon (background) modes.
  • Remember that signals are delivered asynchronously – your program could be in the middle of anything when a signal arrives.

πŸ“¦ Summary

  • The signal module lets you intercept OS-level signals and define custom behavior.
  • Common signals include SIGINT (Ctrl+C), SIGTERM (termination), and SIGALRM (timer).
  • Handlers are simple functions registered with signal.signal().
  • Use signals for graceful shutdowns, timeouts, and configuration reloads.
  • Be mindful of platform limitations and keep handlers lightweight.

Mastering the signal module gives you fine-grained control over how your Python scripts respond to system eventsβ€”a critical skill for writing production-ready tools and services.

Interactive Views

You are currently in πŸ“š All-in-One mode. Use the tabs at the top to switch to πŸ“– Theory Only or πŸ’» Code Only views.

The Python signal module lets engineers handle operating system signals β€” asynchronous notifications sent to a running process.

πŸ›‘ Example 1: Listing available signals on your system

This example shows how to see all signal names defined in the module.

import signal

available_signals = dir(signal)
print(available_signals[:10])

πŸ“€ Output: ['SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 'SIGILL', 'SIGINT']


⏰ Example 2: Setting an alarm signal with SIGALRM

This example demonstrates how to schedule a signal to be sent after a fixed number of seconds.

import signal
import time

def alarm_handler(signum, frame):
    print("Alarm went off!")

signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(2)

print("Waiting for alarm...")
time.sleep(3)

πŸ“€ Output: Waiting for alarm...
πŸ“€ Output: Alarm went off!


⌨️ Example 3: Ignoring the interrupt signal (SIGINT)

This example shows how to make a process ignore Ctrl+C keyboard interrupts.

import signal
import time

def ignore_interrupt(signum, frame):
    print("Interrupt received, but ignoring it")

signal.signal(signal.SIGINT, ignore_interrupt)

print("Press Ctrl+C β€” I will ignore it")
time.sleep(5)
print("Done β€” still running")

πŸ“€ Output: Press Ctrl+C β€” I will ignore it
πŸ“€ Output: (if you press Ctrl+C) Interrupt received, but ignoring it
πŸ“€ Output: Done β€” still running


⏱️ Example 4: Using SIGALRM to timeout a long operation

This example demonstrates a practical use of signals to enforce a time limit on code execution.

import signal

def timeout_handler(signum, frame):
    raise TimeoutError("Operation took too long")

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(3)

try:
    print("Starting slow operation...")
    import time
    time.sleep(5)
    print("Operation completed")
except TimeoutError as e:
    print(f"Timed out: {e}")
finally:
    signal.alarm(0)

πŸ“€ Output: Starting slow operation...
πŸ“€ Output: Timed out: Operation took too long


πŸ”„ Example 5: Handling SIGCHLD to detect child process exit

This example shows how to catch when a child process finishes.

import signal
import os
import time

def child_done(signum, frame):
    print("Child process has exited")

signal.signal(signal.SIGCHLD, child_done)

pid = os.fork()
if pid == 0:
    print("Child process running")
    time.sleep(1)
    os._exit(0)
else:
    print("Parent waiting for child")
    time.sleep(2)
    print("Parent done")

πŸ“€ Output: Parent waiting for child
πŸ“€ Output: Child process running
πŸ“€ Output: Child process has exited
πŸ“€ Output: Parent done


Comparison Table

Signal Name Purpose Typical Use Case
SIGALRM Timer expiration Timeout enforcement
SIGINT Keyboard interrupt Graceful shutdown
SIGCHLD Child process status change Process monitoring
SIGTERM Termination request Clean program exit
SIGKILL Force kill (cannot be caught) Emergency stop