Linux Process Management: ps, top, htop, kill, and More
Understanding process management is critical for every Linux user and administrator. Every running program is a process, and Linux provides a rich set of tools to inspect, monitor, prioritize, and terminate them.
Viewing Processes
ps (Process Snapshot)
# All processes with full format
ps aux
# All processes with standard format
ps -ef
# Show processes for a specific user
ps -u username
ps aux | grep username
# Show process tree
ps auxf
ps -ejH
# Custom output format
ps -eo pid,ppid,cmd,%mem,%cpu,user,start,etime
# Find process by name
ps aux | grep nginx
pgrep nginx # just PIDs
pgrep -u www-data nginx # by user + name
# Show threads of a process
ps -T -p 1234
ps -eLf | grep process_name # all threads system-widetop (Dynamic Process View)
top # interactive process viewer
# Interactive commands within top:
# h — help
# k — kill a process
# r — renice a process
# u — filter by user
# M — sort by memory
# P — sort by CPU
# 1 — toggle CPU/core view
# q — quit
# Batch mode (one-shot)
top -b -n 1 | head -20
# Monitor specific PID
top -p 1234
# Set update interval
top -d 2 # update every 2 secondshtop (Enhanced Viewer)
htop # color-coded, scrollable, mouse support
# Key differences from top:
# Color-coded CPU/memory bars
# Scrollable process list
# Tree view (F5)
# Easier filtering (F4)
# Mouse support
# Customizable columnsProcess States
# Process state codes (from ps STAT column):
# R — Running or runnable (on run queue)
# S — Interruptible sleep (waiting for an event)
# D — Uninterruptible sleep (I/O wait — cannot be killed)
# Z — Zombie (terminated but not reaped by parent)
# T — Stopped (by job control or debugger)
# t — Tracing stop (debugger)
# X — Dead (should not be visible)
# See process states
ps aux | awk '{print $8}' | sort | uniq -c
# Uninterruptible sleep (D state) usually indicates I/O bottleneck
# Zombies (Z state) indicate parent process not calling wait()Signals
Signals are the primary mechanism for inter-process communication and control:
# Common signals
# 1 SIGHUP — Hangup (reload config)
# 2 SIGINT — Interrupt (Ctrl+C)
# 3 SIGQUIT — Quit (with core dump)
# 9 SIGKILL — Kill (cannot be caught/ignored)
# 15 SIGTERM — Terminate (graceful shutdown — default kill)
# 18 SIGCONT — Continue (after SIGSTOP)
# 19 SIGSTOP — Stop (cannot be caught/ignored)
# 20 SIGTSTP — Terminal stop (Ctrl+Z)
# Graceful termination
kill 1234 # SIGTERM (default)
kill -15 1234 # SIGTERM explicit
# Force kill
kill -9 1234 # SIGKILL — last resort
kill -SIGKILL 1234
# Reload configuration
kill -HUP 1234 # SIGHUP — nginx reload
kill -1 1234
# Stop and continue
kill -STOP 1234 # pause
kill -CONT 1234 # resume
# Kill by name
pkill nginx # sends SIGTERM
pkill -9 nginx # sends SIGKILL
killall nginx # same as pkill (SIGTERM)Signal Best Practices
# Always try SIGTERM (15) first — allows graceful shutdown
# Use SIGKILL (9) only as last resort (cannot clean up)
# SIGHUP (1) for configuration reloads
# Example: graceful Nginx restart
sudo nginx -s reload # sends SIGHUP to master
# Equivalent: kill -HUP $(cat /var/run/nginx.pid)Process Priorities (nice and renice)
Linux uses a priority system from -20 (highest) to 19 (lowest):
# Start a process with lower priority
nice -n 10 ./heavy-task.sh
nice -10 ./heavy-task.sh # same as above
# Start with higher priority (requires root)
sudo nice --10 ./important-task.sh
# Change priority of running process
renice -n 5 -p 1234 # lower priority
sudo renice -n -5 -p 1234 # higher priority (root)
# Renice all processes of a user
renice -n 10 -u username
# View nice values
ps -eo pid,ni,cmd | sort -k 2
top # NI column shows nice value
# Show processes by priority
ps -eo pid,ni,pcpu,cmd --sort=niBackground Jobs
# Run command in background
long-running-command &
# Background current process (Ctrl+Z then bg)
# Ctrl+Z suspends the process (SIGTSTP)
bg # resume in background
# Bring to foreground
fg # most recent background job
fg %2 # specific job number
# List jobs
jobs
jobs -l # with PIDs
# Run command immune to hangups (survive logout)
nohup long-running-command &
# Disown (remove from job control)
long-running-command &
disown # survives terminal close
# Screen/tmux (persistent sessions)
screen -S mysession
# Detach: Ctrl+A, D
# Reattach: screen -r mysession
tmux new -s mysession
# Detach: Ctrl+B, D
# Reattach: tmux attach -t mysessionSystem Monitoring
# Overall system load
uptime # load average (1/5/15 minutes)
cat /proc/loadavg
w # who is logged in and what they're doing
# Memory usage
free -h # human-readable
free -m # MB
cat /proc/meminfo
# Disk I/O statistics
iostat -x 2 # extended, every 2 seconds
iotop # per-process I/O (requires root)
iostat -x -p sda 5 # per-partition stats
# Open file descriptors
lsof # list open files
lsof -p 1234 # by PID
lsof -i :8080 # by port
lsof -u username # by user
cat /proc/sys/fs/file-nr # system-wide open file count
# System limits for current process
ulimit -a # show all limits
ulimit -n # max open files
ulimit -u # max user processesReal-World Implementation Tips
Production Considerations
When moving from development to production, several factors become critical. Error handling should be comprehensive — every external call (database, API, file system) should have proper error checking, logging, and retry logic where appropriate. Performance monitoring through metrics and structured logging helps identify bottlenecks before they affect users.
Testing Strategy
A thorough testing approach combines multiple levels:
- Unit tests verify individual functions and methods in isolation
- Integration tests validate that components work together correctly
- Edge case tests cover boundary conditions, empty inputs, and error states
- Performance tests ensure the system meets latency and throughput requirements
Test data should be realistic but controlled. Mock external dependencies to make tests fast and deterministic. Aim for tests that are independent, repeatable, and fast enough to run on every commit.
Documentation
Good documentation is essential for maintainable code. Follow these principles:
- Document the “why” not just the “what” — explain design decisions
- Keep examples up to date with the code
- Include usage examples for public APIs
- Document configuration options and their defaults
- Explain error conditions and recovery strategies
Security Best Practices
Security should be considered throughout development:
- Validate all inputs at system boundaries
- Use parameterized queries for database access
- Store secrets in environment variables or secret managers
- Keep dependencies updated to patch vulnerabilities
- Apply the principle of least privilege
Performance Optimization
Optimize based on measured data, not assumptions:
- Profile before optimizing — identify actual bottlenecks
- Measure the impact of each change
- Consider the trade-off between speed and readability
- Cache expensive operations with appropriate invalidation
- Use connection pooling for database and network resources
Monitoring and Observability
Production systems need visibility:
- Structured logging with correlation IDs for request tracking
- Metrics for latency, throughput, error rates, and resource usage
- Health check endpoints for load balancers and orchestration
- Distributed tracing for request flows across services
- Alerts for anomaly detection based on baselines
These patterns apply across all programming languages and frameworks. The specific implementation varies, but the principles remain consistent.
FAQ
Q: What should I do about zombie processes?
A: Zombies cannot be killed — they are already dead. The parent process must call wait() to reap them. If the parent is broken, kill the parent (zombie children are reaped by init/PID 1).
Q: What is the difference between SIGTERM and SIGKILL? A: SIGTERM (15) allows the process to clean up (close files, release resources). SIGKILL (9) kills immediately — the process cannot catch or ignore it. Always try SIGTERM first.
Q: Why is my process in D (uninterruptible sleep) state? A: D state means the process is waiting for I/O (disk, network). It cannot be killed until the I/O completes. This usually indicates a slow storage device or NFS server issue.
Q: How do I limit CPU usage of a process?
A: Use cpulimit (tool), nice/renice for priority, or cgroups for resource limits. Docker also provides CPU limits.
Q: What is PID 1 and why is it special? A: PID 1 is the init process (systemd on modern systems). It is the first process started by the kernel and adopts orphaned children. It must handle reaping zombies.
Q: How do I find which process is using the most memory?
A: Use ps aux --sort=-%mem | head or press M in top/htop to sort by memory.
For a comprehensive overview, read our article on Bash Scripting Basics.
For a comprehensive overview, read our article on Cron Jobs Guide.