Complete guide to Python's os.kill function covering process signaling, termination, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.kill function, which sends signals to processes. We’ll cover signal types, process IDs, and practical process management examples.
The os.kill function sends a specified signal to a process identified by its process ID (PID). It’s a wrapper around the Unix kill system call.
Key parameters: pid (process ID to signal), sig (signal number to send). Common signals include SIGTERM (15), SIGKILL (9), and SIGHUP (1).
SIGTERM (signal 15) is the default termination signal. It allows the process to perform cleanup before exiting. This is the polite way to request termination.
terminate_process.py
import os import signal import time import subprocess
process = subprocess.Popen([“python”, “-c”, “while True: pass”])
print(f"Process started with PID: {process.pid}")
time.sleep(2) os.kill(process.pid, signal.SIGTERM)
process.wait() print(f"Process terminated with return code: {process.returncode}")
This example starts a Python process that runs indefinitely, then sends SIGTERM to request termination. The process can catch and handle SIGTERM.
The subprocess.Popen.wait() ensures we wait for the process to actually terminate after sending the signal.
SIGKILL (signal 9) immediately terminates a process without cleanup. The process cannot catch or ignore this signal. Use as a last resort.
kill_process.py
import os import signal import subprocess import time
cmd = ’’' import signal, time signal.signal(signal.SIGTERM, signal.SIG_IGN) while True: time.sleep(1) ’’' process = subprocess.Popen([“python”, “-c”, cmd])
print(f"Process started with PID: {process.pid}")
os.kill(process.pid, signal.SIGTERM) time.sleep(1)
if process.poll() is None: print(“SIGTERM failed, sending SIGKILL”) os.kill(process.pid, signal.SIGKILL) process.wait()
print(f"Process terminated with return code: {process.returncode}")
This shows how SIGKILL can terminate a process that ignores SIGTERM. The process.poll() checks if the process is still running after SIGTERM.
SIGKILL should be used sparingly as it doesn’t allow for proper cleanup of resources like open files or network connections.
SIGHUP (signal 1) traditionally indicates a terminal disconnect, but is often used to reload configuration. Many daemons reload config on SIGHUP.
reload_config.py
import os import signal import subprocess import time
cmd = ’’' import signal, time
def handle_hup(signum, frame): print(“Received SIGHUP, reloading config”)
signal.signal(signal.SIGHUP, handle_hup) while True: time.sleep(1) ’’' process = subprocess.Popen([“python”, “-c”, cmd], stdout=subprocess.PIPE, universal_newlines=True)
print(f"Process started with PID: {process.pid}")
time.sleep(1) os.kill(process.pid, signal.SIGHUP)
print(process.stdout.readline())
os.kill(process.pid, signal.SIGTERM) process.wait()
This demonstrates a process that catches SIGHUP to reload configuration. The subprocess captures stdout so we can see the reload message.
Many server applications like nginx and Apache use SIGHUP for graceful configuration reloading without restarting.
Negative PIDs send signals to entire process groups. This is useful for terminating all child processes together with their parent.
process_group.py
import os import signal import subprocess import time
cmd = ’’' import os, time, subprocess print(f"Parent PID: {os.getpid()}") children = [subprocess.Popen([“sleep”, “60”]) for _ in range(3)] [print(f"Child PID: {c.pid}") for c in children] time.sleep(60) ’’' process = subprocess.Popen([“python”, “-c”, cmd], stdout=subprocess.PIPE, universal_newlines=True)
parent_pid = int(process.stdout.readline().split(":")[1]) child_pids = [int(process.stdout.readline().split(":")[1]) for _ in range(3)]
print(f"Parent PID: {parent_pid}") print(f"Child PIDs: {child_pids}")
time.sleep(1) os.kill(-parent_pid, signal.SIGTERM)
for pid in [parent_pid] + child_pids: try: os.kill(pid, 0) # Check if process exists print(f"Process {pid} still running!") except ProcessLookupError: print(f"Process {pid} terminated")
This creates a parent process with three child processes. Sending SIGTERM to the negative parent PID terminates the entire process group.
Process groups ensure all related processes can be managed together, which is particularly useful for shell job control and service management.
Signal 0 doesn’t actually send a signal but checks if sending is possible. This can verify if a process exists and if you have permission to signal it.
check_process.py
import os import subprocess import time
process = subprocess.Popen([“sleep”, “10”]) pid = process.pid
print(f"Checking process with PID: {pid}")
try: os.kill(pid, 0) print(f"Process {pid} exists and can be signaled") except ProcessLookupError: print(f"Process {pid} does not exist") except PermissionError: print(f"No permission to signal process {pid}")
process.wait() try: os.kill(pid, 0) except ProcessLookupError: print(f"Process {pid} no longer exists after completion")
This demonstrates using signal 0 to check process existence. The first check succeeds while the process runs, the second fails after completion.
This technique is useful for process monitoring and health checks without actually interrupting the target process.
Python processes can catch and handle signals using the signal module. This example shows a Python process that gracefully handles termination.
signal_handler.py
import os import signal import sys import time
def handle_term(signum, frame): print(f"Received signal {signum}, cleaning up…") # Perform cleanup here time.sleep(1) # Simulate cleanup print(“Cleanup complete, exiting”) sys.exit(0)
signal.signal(signal.SIGTERM, handle_term) signal.signal(signal.SIGHUP, handle_term)
print(f"Running with PID: {os.getpid()}") print(“Send SIGTERM or SIGHUP to trigger handler”)
while True: time.sleep(1) print(“Working…”)
This script registers handlers for SIGTERM and SIGHUP. When either signal is received, the cleanup function runs before exiting.
Signal handlers allow processes to shutdown gracefully, saving state and releasing resources properly when terminated.
On Windows, os.kill has limited functionality compared to Unix:
Only SIGTERM is supported (treated as TerminateProcess)
No process groups (negative PIDs don’t work)
No other signal numbers are supported
Works only on processes created by same user
Permission requirements: Need permission to signal target process
PID reuse: PIDs can be recycled after process termination
Race conditions: Process may terminate between check and signal
Privilege escalation: Some signals can be security sensitive
Windows limitations: Reduced functionality on Windows
Prefer SIGTERM: Allow processes to clean up before exiting
Reserve SIGKILL: Use only when absolutely necessary
Check permissions: Verify you can signal the process first
Handle errors: Process may not exist or you may lack permissions
Consider alternatives: For subprocesses, use Popen methods
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.