Process Management¶
Overview¶
A process is a running instance of a program — identified by a Process ID (PID), owned by a user, consuming CPU and memory, and existing in a parent-child tree rooted at PID 1 (systemd on modern distributions). Administrators monitor processes to diagnose performance issues, terminate runaway jobs, adjust scheduling priority, and understand system behavior during incidents.
This tutorial is part of Module 3: Processes, Services & Packages in the REBASH Academy Linux series. You will list and filter processes, interpret CPU/memory columns, send signals for graceful and forced termination, manage background jobs, and understand zombie and orphan process states.
Prerequisites¶
- Complete User and Group Management or equivalent experience
- A Linux VM or local machine with terminal access
- Optional:
htoppackage for enhanced monitoring (sudo apt install htoporsudo dnf install htop) sudoforreniceon processes owned by other users
Learning Objectives¶
By the end of this tutorial, you will be able to:
- List and filter processes using
ps,pgrep, andpidof - Monitor real-time resource usage with
topandhtop - Send signals with
killand interpret SIGTERM vs SIGKILL behavior - Adjust CPU scheduling priority using
niceandrenice - Manage foreground/background jobs with
&,jobs,fg, andbg - Identify and resolve zombie and orphan process conditions
Theory¶
Process states¶
| State | Code | Meaning |
|---|---|---|
| Running | R | Executing or ready to run |
| Sleeping | S | Waiting for an event (interruptible) |
| Disk sleep | D | Uninterruptible I/O wait |
| Stopped | T | Paused (SIGSTOP / Ctrl+Z) |
| Zombie | Z | Exited but entry remains until parent reaps |
| Dead | X | Fully terminated (transient) |
View states in ps aux (STAT column) or ps -eo pid,stat,cmd.
The process tree¶
Every process except PID 1 has a parent (PPID). When a parent dies before its child, the child becomes an orphan and is adopted by PID 1 (systemd), which reaps it on exit.
A zombie is a process that has exited but whose parent has not called wait() to read its exit status. Zombies consume no CPU or memory (only a kernel slot) but indicate a buggy parent. Kill the parent, not the zombie, to clean up.
CPU priority (nice values)¶
Nice values range from -20 (highest priority) to +19 (lowest). Default is 0.
- Regular users can only increase nice (lower priority):
nice -n 10 command - Root can set any value:
nice -n -5 command reniceadjusts running processes
Nice affects CPU scheduling, not I/O priority (see ionice for disk).
Signals¶
Signals are software interrupts delivered by the kernel. Common signals:
| Signal | Number | Default action | Typical use |
|---|---|---|---|
| SIGHUP | 1 | Terminate | Reload config (many daemons trap this) |
| SIGINT | 2 | Terminate | Ctrl+C in terminal |
| SIGTERM | 15 | Terminate | Graceful shutdown (default kill) |
| SIGKILL | 9 | Kill | Force immediate termination |
| SIGSTOP | 19 | Stop | Pause process (cannot be caught) |
| SIGCONT | 18 | Continue | Resume stopped process |
Always try SIGTERM first. SIGKILL cannot be caught or ignored — resources (temp files, locks) may be left inconsistent.
Background jobs and job control¶
Shell job control tracks pipelines started in the current session:
command &— run in backgroundCtrl+Z— suspend foreground jobjobs— list jobsfg %1— bring job 1 to foregroundbg %1— resume job 1 in background
Job numbers (%1) are shell-local; PIDs are system-wide.
ps output formats¶
| Command | Style | Notes |
|---|---|---|
ps aux | BSD | a=all users, u=user-oriented, x=no terminal |
ps -ef | UNIX | Full format listing |
ps -eo pid,ppid,stat,cmd | Custom | Script-friendly columns |
top and htop show live snapshots sorted by CPU (press P) or memory (M).
Hands-on Lab¶
Step 1 – List processes with ps¶
ps aux | head -8
ps -ef | grep -E "PID|systemd" | head -5
ps -eo pid,ppid,user,stat,pcpu,pmem,cmd --sort=-pcpu | head -10
Expected output:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169000 12000 ? Ss 09:00 0:02 /sbin/init
root 456 0.0 0.2 50000 8000 ? Ss 09:00 0:00 sshd: /usr/sbin/sshd
...
PID PPID USER STAT %CPU %MEM CMD
1234 1 root Ss 0.0 0.1 /usr/lib/systemd/systemd --user
Top CPU consumers appear first in the custom sort output.
Step 2 – Find processes by name¶
Expected output:
The [s]shd trick prevents grep from matching itself.
Step 3 – Monitor with top (batch mode)¶
Expected output:
top - 14:30:01 up 5:30, 1 user, load average: 0.08, 0.04, 0.01
Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.3 us, 1.1 sy, 0.0 ni, 96.4 id, 0.2 wa, 0.0 hi, 0.0 si
MiB Mem : 7945.0 total, 3200.0 free, 2100.0 used, 2645.0 buff/cache
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 user 20 0 123456 45678 8901 S 5.0 0.6 0:12.34 python3
Note the zombie count in the Tasks line — should be 0 on healthy systems.
Step 4 – Start, background, and manage jobs¶
Expected output:
Bring job 1 to foreground, suspend, resume in background:
Expected output after Ctrl+Z:
Step 5 – Send signals gracefully and forcefully¶
sleep 600 &
SLEEP_PID=$!
kill -0 $SLEEP_PID && echo "Process $SLEEP_PID exists"
kill $SLEEP_PID # SIGTERM (default)
wait $SLEEP_PID 2>/dev/null; echo "Exit code: $?"
Expected output:
Exit code 143 = 128 + 15 (SIGTERM). Test SIGKILL on a stubborn process:
Expected output: Exit code: 137 (128 + 9).
Step 6 – Adjust priority with nice and renice¶
nice -n 10 sleep 300 &
NPID=$!
ps -o pid,ni,cmd -p $NPID
sudo renice -n -5 -p $NPID
ps -o pid,ni,cmd -p $NPID
kill $NPID
Expected output:
PID NI CMD
5681 10 sleep 300
5681 (process ID) old priority 10, new priority -5
PID NI CMD
5681 -5 sleep 300
Only root can lower nice below 0.
Step 7 – Observe a zombie process¶
Run this in a subshell to create a short-lived zombie:
bash -c 'sleep 1 & exec sleep 30' &
ZOMBIE_PARENT=$!
sleep 2
ps -o pid,ppid,stat,cmd -p $ZOMBIE_PARENT
ps aux | awk '$8 ~ /Z/ {print}'
kill $ZOMBIE_PARENT
sleep 1
ps aux | awk '$8 ~ /Z/ {print}' || echo "No zombies"
Expected output:
Killing the parent allows systemd (PID 1) to reap the zombie.
Step 8 – Inspect process tree¶
Expected output:
Shows parent-child relationships with PIDs in parentheses.
Commands¶
| Command | Description |
|---|---|
ps aux | Snapshot of all processes (BSD format) |
ps -ef | Full listing (UNIX format) |
ps -p PID -o stat,cmd | Show state and command for one PID |
pgrep -l NAME | Find PIDs by name, show name |
pidof PROCESS | Print PIDs of running binary |
top | Interactive real-time monitor |
top -b -n 1 | Single batch snapshot (scripting) |
htop | Enhanced interactive monitor |
kill PID | Send SIGTERM (15) to process |
kill -9 PID | Send SIGKILL (force) |
kill -HUP PID | Send SIGHUP (reload on many daemons) |
kill -0 PID | Test if process exists (no signal sent) |
killall NAME | Signal all processes matching name |
nice -n 10 CMD | Start command with lower priority |
renice -n 5 -p PID | Change nice of running process |
jobs -l | List shell background jobs with PIDs |
fg %N | Bring job N to foreground |
bg %N | Resume stopped job N in background |
pstree -p | Display process tree with PIDs |
lsof -p PID | List open files for a process |
Code Examples¶
Find top memory consumers¶
#!/bin/bash
# top-mem.sh — report top 10 processes by RSS memory
set -euo pipefail
echo "PID %MEM RSS(KB) COMMAND"
ps -eo pid,pmem,rss,cmd --sort=-rss | head -11 | tail -10 \
| awk '{printf "%-6s %-6s %-8s %s\n", $1, $2, $3, $4" "$5" "$6}'
Graceful shutdown with timeout¶
#!/bin/bash
# graceful-stop.sh — SIGTERM then SIGKILL after timeout
set -euo pipefail
PID="${1:?Usage: $0 <pid>}"
TIMEOUT="${2:-15}"
kill -TERM "$PID" 2>/dev/null || { echo "Process not found"; exit 1; }
for i in $(seq 1 "$TIMEOUT"); do
kill -0 "$PID" 2>/dev/null || { echo "Stopped gracefully"; exit 0; }
sleep 1
done
echo "Sending SIGKILL"
kill -9 "$PID"
Detect zombie processes¶
#!/bin/bash
# check-zombies.sh — alert if zombies present
set -euo pipefail
ZOMBIES=$(ps aux | awk '$8 ~ /Z/ {count++} END {print count+0}')
if [[ "$ZOMBIES" -gt 0 ]]; then
echo "WARNING: $ZOMBIES zombie process(es) detected"
ps aux | awk '$8 ~ /Z/ {print}'
exit 1
fi
echo "OK: no zombies"
Common Mistakes¶
Using kill -9 as the first resort
SIGKILL bypasses cleanup handlers. Databases, web servers, and file writers may corrupt data. Always try SIGTERM and wait before escalating.
Killing zombie PIDs directly
Zombies are already dead — killing them has no effect. Terminate or restart the parent process that failed to reap.
Confusing shell job IDs with PIDs
kill %1 is shell job control; kill 5680 uses a PID. Mixing them causes "no such process" errors.
Monitoring only CPU, ignoring D state
Processes in uninterruptible sleep (D) indicate stuck I/O — often NFS or disk issues. High D-state counts need storage investigation, not more CPU.
Best Practices¶
Use SIGTERM then escalate
Production runbooks: kill -15 PID, wait 30s, verify with kill -0 PID, then kill -9 PID if needed.
Correlate with systemd for services
Prefer systemctl stop UNIT over raw kill for managed services — systemd handles dependencies and restart policy.
Set ulimits and cgroup limits
Prevent fork bombs and runaway memory with /etc/security/limits.conf and systemd MemoryMax= in unit files.
Capture ps snapshots during incidents
ps auxww > /tmp/ps-$(date +%s).txt preserves full command lines for post-mortem analysis.
Use htop filters in busy systems
Press F4 in htop to filter by process name; / searches in top.
Troubleshooting¶
| Issue | Cause | Solution |
|---|---|---|
| Process won't die | Ignoring SIGTERM or D-state I/O | Wait; check stat; use SIGKILL; investigate stuck NFS/disk |
| Zombie count growing | Parent not calling wait() | Identify parent with ps -o ppid= -p ZPID; restart parent service |
| High load, low CPU | I/O wait or many blocked threads | Check wa in top; use iostat, iotop |
| Cannot renice | Insufficient privileges | Regular users cannot lower nice below current; use sudo |
| kill: Operation not permitted | Different user's process | Use sudo or kill as root |
| Background job exits on logout | SIGHUP sent to shell | Use nohup, disown, or systemd service instead |
| Wrong process killed | Name collision with killall | Use PID from pgrep instead of killall |
Summary¶
- Processes form a tree under PID 1; each has a state, owner, and resource consumption profile.
- Use
ps,pgrep, andtop/htopto monitor; sort by CPU and memory to find bottlenecks. - Send SIGTERM for graceful shutdown; reserve SIGKILL for unresponsive processes.
- Nice values adjust CPU priority; root can favor critical workloads with negative nice.
- Shell job control manages background tasks in interactive sessions; use systemd for persistent services.
- Zombies indicate parent bugs — fix the parent. Orphans are adopted by systemd automatically.
Interview Questions¶
1. What is the difference between SIGTERM and SIGKILL?
Sample answer: SIGTERM (15) asks a process to terminate gracefully — it can catch the signal, flush buffers, and close connections. SIGKILL (9) forces immediate termination and cannot be caught. Always try SIGTERM first.
2. How do you find the top 5 CPU-consuming processes?
Sample answer: ps -eo pid,pcpu,cmd --sort=-pcpu | head -6 or interactively in top by pressing P to sort by CPU.
3. What is a zombie process, and how do you remove it?
Sample answer: A zombie (state Z) has exited but remains in the process table until its parent calls wait(). It uses no CPU/memory. Kill or restart the parent process; killing the zombie PID does nothing.
4. What happens to orphan processes?
Sample answer: When a parent dies before its child, the child is reparented to PID 1 (systemd). Systemd becomes the new parent and reaps the child when it exits.
5. Explain nice value -20 vs +19.
Sample answer: Nice ranges from -20 (highest CPU priority) to +19 (lowest). Default is 0. Regular users can only increase nice (lower priority). Root can set -20 for critical processes.
6. What does exit code 137 indicate?
Sample answer: 137 = 128 + 9, meaning the process was killed by SIGKILL. Similarly, 143 = 128 + 15 (SIGTERM).
7. How do you run a command in the background and keep it after logout?
Sample answer: For ad-hoc tasks: nohup command & disown. For production workloads, create a systemd service unit instead of relying on nohup.
8. What is the difference between ps aux and ps -ef?
Sample answer: Both list processes but use different column sets and syntax (BSD vs UNIX style). ps aux shows %CPU/%MEM; ps -ef shows full command with PPID in a familiar UNIX format.
9. When would you see a process in D state?
Sample answer: D (uninterruptible sleep) means the process waits on I/O that cannot be interrupted — typically disk or NFS. Many D-state processes suggest storage or network filesystem problems.
10. How do you verify a PID exists without killing it?
Sample answer: kill -0 PID sends signal 0, which performs error checking without sending a signal. Also: ps -p PID or test -d /proc/PID.
Related Tutorials¶
- Linux – Category Overview
- User and Group Management (previous)
- systemd Service Management (next)
- Log Management with journalctl
- Troubleshooting Linux Systems
- Learning Paths – DevOps Engineer