Linux process management is a core sysadmin skill — whether you are hunting a runaway CPU hog, gracefully stopping a misbehaving daemon, or tuning scheduling priority for a batch job, knowing how to inspect and control processes keeps systems healthy. This guide covers the full toolkit: ps aux, top, htop, kill signals, nice/renice, background jobs, and the /proc filesystem, with practical examples you can use immediately.
Prerequisites
- A Linux system (any distribution — Ubuntu, Debian, RHEL, Fedora, Arch)
- Terminal access with a regular user account (some commands require
sudo) htopinstalled (apt install htopordnf install htop) for the htop section- Basic familiarity with the command line
Listing Processes with ps
ps (process status) is the classic snapshot tool. It reads /proc and prints current process state at the moment you run it — unlike top, it does not refresh.
ps aux — BSD syntax
ps aux
| Column | Meaning |
|---|---|
| USER | Owner of the process |
| PID | Process ID |
| %CPU | CPU usage since process started |
| %MEM | Resident memory as % of total RAM |
| VSZ | Virtual memory size (KB) |
| RSS | Resident Set Size — actual RAM used (KB) |
| STAT | Process state (R=running, S=sleeping, Z=zombie, T=stopped) |
| COMMAND | Full command line |
Sort by CPU and show the worst offenders:
ps aux --sort=-%cpu | head -20
ps aux --sort=-%mem | head -20
Find processes by name without grep appearing in results:
ps aux | grep '[n]ginx'
ps -ef — UNIX syntax
ps -ef
ps -ef adds the PPID (parent PID) column, which is invaluable when tracing process trees. Use -eH or --forest for a tree view:
ps -eH
ps -ef --forest
Process states decoded
- R — runnable (on CPU or in run queue)
- S — interruptible sleep (waiting for I/O or an event)
- D — uninterruptible sleep (usually disk I/O — cannot be killed)
- Z — zombie (finished but parent has not reaped it)
- T — stopped (Ctrl+Z or SIGSTOP)
A high count of D state processes points to I/O saturation. Z zombies are harmless in small numbers but indicate a parent not calling wait().
Interactive Monitoring with top
top gives a live, auto-refreshing view (default 3-second interval). Launch it, then use these keyboard shortcuts:
| Key | Action |
|---|---|
P | Sort by CPU usage |
M | Sort by memory usage |
T | Sort by cumulative CPU time |
k | Prompt for a PID to kill |
r | Renice a process |
1 | Toggle per-CPU-core view |
u | Filter by username |
f | Open field manager (add/remove columns) |
q | Quit |
d | Change refresh interval (seconds) |
H | Show individual threads instead of processes |
The header summarizes load averages (1, 5, 15 minutes), CPU breakdown (us/sy/id/wa), and memory. A wa (I/O wait) above 10-15% usually means a storage bottleneck.
Run top non-interactively to capture a snapshot:
top -bn1 | head -30
Richer Process Monitoring with htop
htop is top with a colour UI, mouse support, and more intuitive controls. After installing and launching it, the function-key bar at the bottom guides you:
| Key | Action |
|---|---|
F2 | Setup (colours, columns, meters) |
F3 | Search process list |
F4 | Filter (show only matching processes) |
F5 | Tree view (process hierarchy) |
F6 | Sort column selector |
F9 | Send signal to selected process |
F10 | Quit |
| Arrow keys | Navigate process list |
Space | Tag a process for bulk action |
htop shows CPU bars per core, memory and swap bars, and lets you scroll horizontally to see long command lines — all things top requires extra keystrokes to reveal.
Process Management Tools Comparison
| Feature | ps | top | htop | pgrep/pkill |
|---|---|---|---|---|
| Live refresh | No | Yes | Yes | No |
| Mouse support | No | Limited | Yes | No |
| Tree view | --forest flag | No | F5 | No |
| Send signals | No | k key | F9 | Directly |
| Filter/search | Pipe to grep | u key | F4/F3 | By pattern/regex |
| Colour output | No | Basic | Rich | No |
| Script-friendly | Excellent | Poor | Poor | Excellent |
| Install required | Built-in | Built-in | Yes | Built-in |
Use ps and pgrep/pkill in scripts; use top or htop interactively on servers.
Sending Signals: kill, pkill, killall
Signals are the primary IPC mechanism for process control. The most important ones:
| Signal | Number | Default action | Use case |
|---|---|---|---|
| SIGTERM | 15 | Graceful terminate | Normal stop — allows cleanup |
| SIGKILL | 9 | Immediate kill | Process ignores SIGTERM |
| SIGHUP | 1 | Reload config | Tell daemons to re-read config |
| SIGSTOP | 19 | Pause (unblockable) | Suspend a process |
| SIGCONT | 18 | Resume | Resume a stopped process |
| SIGUSR1/2 | 10/12 | User-defined | App-specific (e.g., log rotation) |
kill by PID
kill 1234 # SIGTERM — ask nicely
kill -15 1234 # explicit SIGTERM
kill -9 1234 # SIGKILL — no mercy
kill -HUP 1234 # reload config
kill -l # list all signal names
Always try SIGTERM first. Give the process 10-30 seconds, then escalate to SIGKILL if needed.
pkill and pgrep — match by name or pattern
pgrep nginx # list PIDs matching "nginx"
pgrep -u www-data nginx # narrow to a specific user
pkill nginx # SIGTERM all matching processes
pkill -9 nginx # SIGKILL all matching
pkill -HUP nginx # reload nginx config (same as kill -HUP $(pgrep nginx))
killall — kill all instances by exact name
killall firefox
killall -9 java
killall -u jcarlos # kill all processes owned by a user
pkill accepts regex and partial matches; killall requires the exact process name. Prefer pkill in scripts.
Priority Tuning with nice and renice
Linux schedules processes using a niceness value from -20 (highest priority, least nice to others) to 19 (lowest priority). The default is 0. Only root can set negative values.
nice — set priority at launch
nice -n 19 tar -czf backup.tar.gz /var/data # background backup, low priority
nice -n -5 ./realtime-processor # higher priority (needs sudo)
sudo nice -n -10 ./critical-task
renice — adjust a running process
renice -n 10 -p 1234 # lower priority of PID 1234
sudo renice -n -5 -p 1234 # raise priority (root required)
renice -n 15 -u jcarlos # lower priority for all of user's processes
Check current niceness with ps:
ps -o pid,ni,comm -p 1234
Background Jobs: bg, fg, and jobs
The shell’s job control lets you manage multiple tasks without opening extra terminals.
long-running-command & # start directly in background
./script.sh # running in foreground
# press Ctrl+Z to suspend
[1]+ Stopped ./script.sh
jobs # list jobs
# [1]+ Stopped ./script.sh
# [2]- Running long-running-command &
bg %1 # resume job 1 in background
fg %1 # bring job 1 to foreground
fg %2 # bring job 2 to foreground
disown %1 # detach job from shell (survives logout)
nohup ./script.sh & # immune to SIGHUP, output to nohup.out
& and bg still tie the process to the terminal session. Use nohup, screen, or tmux for jobs that must survive logout.
Exploring /proc
Everything ps and top know comes from the /proc virtual filesystem — a live window into the kernel’s process table.
ls /proc/1234/ # all files for PID 1234
cat /proc/1234/status # human-readable status (Name, State, VmRSS, Threads…)
cat /proc/1234/cmdline # full command line (null-separated)
cat /proc/1234/environ # environment variables at launch
ls -l /proc/1234/fd/ # open file descriptors (sockets, pipes, files)
cat /proc/1234/maps # memory map
cat /proc/self/status # info about the current shell process
Useful system-wide /proc files:
cat /proc/loadavg # 1m, 5m, 15m load + running/total processes
cat /proc/meminfo # detailed memory statistics
cat /proc/cpuinfo # per-core CPU details
cat /proc/uptime # system uptime in seconds
Real-World Scenario: Production Server CPU Spike
You have a production web server that suddenly hits 100% CPU. Here is the playbook:
# 1. Identify the culprit
ps aux --sort=-%cpu | head -10
# 2. Get more detail (open files, network connections)
PID=<offending pid>
ls -l /proc/$PID/fd | wc -l # file descriptor count
cat /proc/$PID/status # check threads, memory
cat /proc/$PID/cmdline # exact command
# 3. Try a graceful stop first
kill -15 $PID
sleep 15
# 4. Check if it stopped
ps -p $PID
# 5. Force kill if still running
kill -9 $PID
# 6. If it is a service, restart cleanly
sudo systemctl restart myapp
If the process is a known daemon, kill -HUP may be all you need to clear a stuck state without a full restart.
Gotchas and Edge Cases
SIGKILL cannot be caught or ignored. A process in uninterruptible sleep (state D, waiting on I/O) will not respond to SIGKILL until the I/O completes. In this case, the underlying device or NFS mount is usually the problem.
Zombie processes are not a leak. A zombie (state Z) holds a PID but no resources. It disappears when the parent calls wait(). If zombies accumulate, the parent process is buggy — killing the parent or fixing it is the solution, not killing the zombie directly.
ps %CPU is lifetime average, not current. The %CPU in ps aux is calculated over the process lifetime. A process that spiked yesterday and is now idle shows a low number. Use top or htop for real-time CPU.
nice requires root for negative values. Regular users can only increase niceness (lower priority). Mistyping nice -10 (which bash interprets as flag -1 0) is a common error — always use nice -n 10.
pkill matches substrings. pkill python will kill python3, python2, and any process with “python” in its name — including your editor if it embeds a Python interpreter. Use pgrep first to verify what will be matched.
Job control is per-shell. jobs only shows jobs started in the current shell session. Use ps aux | grep command to find detached processes.
Troubleshooting Common Issues
“Operation not permitted” when killing a process — you are trying to kill a process owned by another user or a root process. Use sudo kill PID.
Process immediately respawns after kill — a supervisor (systemd, supervisord, Docker) is restarting it. Stop the service with sudo systemctl stop service-name instead.
kill -9 does not work — the process is in D state (uninterruptible sleep). Check iostat -x 1 for I/O wait; the block device or network filesystem is stalled. A reboot may be necessary in extreme cases.
htop shows swap usage climbing — your system is memory-pressured. Use ps aux --sort=-%mem | head -10 to find the largest consumers and consider adding swap or restarting the offending application.
Load average high but CPU usage low — processes are waiting on I/O, not CPU. Check iostat, iotop, and D-state counts with ps aux | awk '$8 ~ /D/ {print}'.
Summary
ps auxgives a snapshot sorted by CPU or memory; use--forestfor a tree view.topandhtopprovide live monitoring;htopadds colour, mouse support, and F-key shortcuts.- Signals: always try SIGTERM (15) first, escalate to SIGKILL (9) only if necessary; SIGHUP (1) reloads most daemons.
pkill/pgrepare script-friendly alternatives tokillwhen you know a name rather than a PID.nice/renicetune scheduling priority (-20 to 19); negative values require root.bg/fg/jobsmanage shell job control; usenohuportmuxto survive logout./proc/<PID>/is the source of truth —status,cmdline,fd/, andmapsreveal everything about a running process.