Python Process Management — Core Concepts
What a process is
Every running program on your computer is a process. Each process has a unique PID (process ID), an owner (the user who started it), resource consumption (CPU, memory), and a parent process that created it.
Python provides two main tools for working with processes:
- psutil for inspecting and managing existing processes
- subprocess for launching and controlling new processes
Listing and inspecting processes
Iterating all processes
import psutil
for proc in psutil.process_iter(['pid', 'name', 'username', 'cpu_percent', 'memory_info']):
info = proc.info
mem_mb = info['memory_info'].rss / (1024 * 1024) if info['memory_info'] else 0
print(f"PID {info['pid']:>6} | {info['name']:<25} | "
f"User: {info['username']:<12} | Mem: {mem_mb:.1f} MB")
The attrs parameter in process_iter() tells psutil which fields to collect. This is more efficient than calling individual methods on each process, because psutil batches the system calls.
Getting detailed info on one process
proc = psutil.Process(1234)
print(f"Name: {proc.name()}")
print(f"Status: {proc.status()}")
print(f"Started: {proc.create_time()}")
print(f"CPU: {proc.cpu_percent(interval=1)}%")
print(f"Memory RSS: {proc.memory_info().rss / (1024**2):.1f} MB")
print(f"Threads: {proc.num_threads()}")
print(f"Open files: {len(proc.open_files())}")
print(f"Connections: {len(proc.connections())}")
print(f"Command: {' '.join(proc.cmdline())}")
Finding specific processes
By name
def find_by_name(name: str) -> list[psutil.Process]:
return [p for p in psutil.process_iter(['name'])
if p.info['name'] == name]
nginx_procs = find_by_name('nginx')
By resource usage
def top_memory_processes(count: int = 10) -> list[dict]:
procs = []
for p in psutil.process_iter(['pid', 'name', 'memory_info', 'cpu_percent']):
if p.info['memory_info']:
procs.append({
'pid': p.info['pid'],
'name': p.info['name'],
'memory_mb': p.info['memory_info'].rss / (1024 ** 2),
'cpu': p.info['cpu_percent'],
})
return sorted(procs, key=lambda x: x['memory_mb'], reverse=True)[:count]
By port
def find_by_port(port: int) -> list[dict]:
"""Find processes listening on a specific port."""
results = []
for conn in psutil.net_connections(kind='inet'):
if conn.laddr.port == port and conn.status == 'LISTEN':
try:
proc = psutil.Process(conn.pid)
results.append({
'pid': conn.pid,
'name': proc.name(),
'address': f"{conn.laddr.ip}:{conn.laddr.port}",
})
except psutil.NoSuchProcess:
continue
return results
Controlling processes
Sending signals
import signal
proc = psutil.Process(1234)
# Graceful termination
proc.terminate() # Sends SIGTERM
# Wait for it to exit (with timeout)
try:
proc.wait(timeout=10)
except psutil.TimeoutExpired:
proc.kill() # Force kill with SIGKILL
# Send a custom signal
proc.send_signal(signal.SIGUSR1) # Trigger reload, etc.
Starting new processes
import subprocess
# Simple: run and wait
result = subprocess.run(
['python3', 'worker.py', '--id', '1'],
capture_output=True, text=True, timeout=30
)
# Long-running: start and manage
proc = subprocess.Popen(
['python3', 'server.py'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print(f"Started with PID {proc.pid}")
# Later: check if still running
if proc.poll() is None:
print("Still running")
Process trees
Processes have parent-child relationships. A web server might spawn worker processes, and each worker might spawn helpers:
def process_tree(pid: int, indent: int = 0):
try:
proc = psutil.Process(pid)
mem = proc.memory_info().rss / (1024 ** 2)
print(f"{' ' * indent}PID {pid} | {proc.name()} | {mem:.1f} MB")
for child in proc.children():
process_tree(child.pid, indent + 1)
except psutil.NoSuchProcess:
pass
Common misconception
Many people use os.kill(pid, signal.SIGKILL) as their first choice to stop a process. This is like pulling the power cord — the process gets no chance to clean up (close files, save state, release locks). Always try SIGTERM first and give the process time to shut down gracefully. Use SIGKILL only as a last resort after a timeout.
Practical use cases
- Runaway process killers that terminate processes exceeding CPU or memory thresholds
- Port conflict resolution that finds and optionally kills processes occupying a needed port
- Process restarters that detect a crashed service and restart it
- Resource auditing that logs per-process resource usage for capacity planning
- Deployment scripts that gracefully stop old versions before starting new ones
- Health monitors that verify critical processes are running and alert when they are not
One thing to remember: Python’s process management capabilities let you build the same tools that system administrators use daily — listing processes, finding resource hogs, sending signals for graceful shutdown, and starting new processes — all programmable and automatable.
See Also
- Python Crontab Management How Python can set up automatic timers on your computer — like programming an alarm clock that runs tasks instead of waking you up.
- Python Disk Usage Monitoring How Python helps you keep an eye on your computer's storage — like a fuel gauge that warns you before you run out of space.
- Python Log Rotation Management Why your program's diary needs page limits — and how Python keeps log files from eating all your disk space.
- Python Network Interface Monitoring How Python watches your computer's network connections — like having a traffic counter on every road leading to your house.
- Python Psutil System Monitoring How Python's psutil library lets your program check on your computer's health — like a doctor with a stethoscope for your machine.