Introduction – What, Why, and Who This Is For
If you ever run a command that never returns, a script that hangs, or a GUI app that refuses to close, you’re dealing with a stuck process. Knowing how to spot it, inspect its details, and terminate it cleanly is a core skill for any Linux user.
This tutorial is written for beginners to intermediate developers who use a terminal daily (or want to start). We’ll cover the most common tools:
| Tool | Primary use |
|---|---|
ps |
List processes, filter by name, user, or state |
kill |
Send signals (e.g., SIGTERM, SIGKILL) to a process |
htop |
Interactive, color‑rich process viewer |
&, jobs, fg, bg |
Run commands in the background and manage them |
By the end you’ll be able to find a rogue process, stop it without harming the system, and run long‑running jobs without blocking your shell.
Part 1 – Inspecting Processes with ps
1.1 The basic ps command
ps aux
a– show processes for all users with a terminalu– display the user/owner columnx– include processes without a controlling terminal (daemons)
The output looks like this:
| USER | PID | %CPU | %MEM | VSZ | RSS | TTY | STAT | START | TIME | COMMAND |
|---|---|---|---|---|---|---|---|---|---|---|
| alice | 1245 | 0.0 | 0.1 | 3456 | 560 | pts/0 | S | 09:12 | 0:00 | bash |
| root | 1 | 0.1 | 0.5 | 8320 | 1024 | ? | Ss | 09:00 | 0:03 | /sbin/init |
STATcodes are important:R– runningS– sleeping (waiting for I/O)D– uninterruptible sleep (usually I/O)Z– zombie (already dead, waiting for parent)T– stopped (by job control or a signal)
1.2 Filtering by name or PID
ps -C python # show all processes whose executable name is "python"
ps -p 1245,1289 # show only the listed PIDs
ps aux | grep myapp # quick text search (beware of the grep process itself)
1.3 Using ps with --forest for a tree view
ps -ef --forest | grep nginx
The tree view helps you see parent‑child relationships, which is useful when a child process is stuck but the parent is still alive.
Part 2 – Killing Processes Safely with kill
2.1 Signals 101
| Signal | Number | Typical use |
|---|---|---|
SIGTERM |
15 | Graceful shutdown (default) |
SIGINT |
2 | Interrupt (Ctrl‑C) |
SIGKILL |
9 | Forceful kill, cannot be caught |
SIGSTOP |
19 | Pause a process (like Ctrl‑Z) |
SIGCONT |
18 | Resume a stopped process |
2.2 The usual workflow
- Identify the PID with
psorhtop. -
Ask nicely:
kill -TERM 1245 # or simply: kill 1245 -
Verify that the process disappeared:
ps -p 1245 && echo "still alive" || echo "gone" -
Escalate only if needed:
kill -KILL 1245 # last resort
2.3 Killing by name with pkill and killall
pkill -f "my long script.py" # matches the full command line
killall -9 nginx # kill all processes named nginx
pkill uses pattern matching, while killall matches the executable name exactly. Both accept signal options (-SIGTERM, -9, etc.).
2.4 Preventing accidental kills
- Use
-i(interactive) withkillallto confirm each PID. - Double‑check the PID with
ps -p $PID -o pid,ppid,user,stat,command.
Part 3 – Interactive Inspection with htop
htop is not required for the tutorial, but it speeds up the “find‑and‑kill” loop.
sudo apt-get install htop # Debian/Ubuntu
htop
Key shortcuts:
| Key | Action |
|---|---|
F3 |
Search (type part of the command) |
F4 |
Filter (show only matching rows) |
F5 |
Tree view (parent‑child) |
F9 |
Kill – choose a signal from a menu |
Space |
Tag multiple processes for batch actions |
s |
Sort by column (CPU, MEM, TIME, etc.) |
You can also start htop with a filter:
htop -p $(pgrep -d',' python)
This opens htop showing only Python processes, making it easy to spot the one that’s stuck.
Part 4 – Running and Managing Background Jobs
4.1 The & operator
Appending & runs a command asynchronously, returning control to the shell immediately.
sleep 120 & # runs for 2 minutes in the background
echo "I can type now"
The shell prints a job number and PID, e.g., [1] 5678.
4.2 Listing jobs
jobs # shows jobs started in the current shell
Output example:
[1]+ Running sleep 120 &
[2]- Stopped vim
4.3 Bringing a job to the foreground
fg %1 # bring job 1 back to the foreground
4.4 Sending a job to the background (after stopping)
Ctrl+Z # stop the current foreground job (SIGSTOP)
bg %2 # resume job 2 in the background
4.5 Disowning a job
If you want a background job to survive after you close the terminal, use disown:
sleep 300 & # start a long sleep
disown %1 # remove it from the shell's job table
Now jobs won’t list it, but the process keeps running (check with ps).
Hands‑On Example – Find, Inspect, and Kill a Stuck Process
Below is a reproducible scenario you can try on any Linux box.
#!/usr/bin/env bash
# demo_stuck.sh – creates a process that loops forever
while true; do
echo "Working..." # writes to stdout every second
sleep 1
done
-
Run the script in the background
chmod +x demo_stuck.sh ./demo_stuck.sh &You’ll see something like
[1] 12345. -
Verify it’s running
ps -p 12345 -o pid,stat,commandExpected output:
PID STAT COMMAND 12345 S ./demo_stuck.sh -
Inspect with
htop(optional)htop -p 12345Look at the
%CPUcolumn – a tight loop often shows near‑100% CPU. -
Kill it gracefully
kill 12345 # sends SIGTERM sleep 1 ps -p 12345 && echo "still alive" || echo "gone"If the script ignored SIGTERM (unlikely here), force it:
kill -9 12345 -
Clean up the job table
jobs # should show no jobs, because the process is gone
You’ve now gone from “a runaway script” to “clean termination” using only terminal commands.
Common Mistakes & Troubleshooting
- Killing the wrong PID – Always double‑check with
ps -p $PID -o pid,command. - Using
kill -9first – This bypasses any cleanup code. PreferSIGTERM(default) and only fall back toSIGKILLif the process refuses to exit. - Forgetting the
&when you meant to background – The shell will block, making you think the command hung. Add&and re‑run. jobsshows “Stopped” but the process still consumes CPU – The process may have been resumed by another shell or a script. Useps -o pid,stat,commandto see the real state.htopnot installed – Install via your distro’s package manager (sudo dnf install htop,sudo pacman -S htop, etc.) or fall back totop.
Try It Yourself
Create a background job that writes a large file, then stop it, move it to the background, and finally kill it:
dd if=/dev/zero of=bigfile bs=1M count=100 &
# 1. Press Ctrl+Z to stop it
# 2. Run `bg %1` to resume in background
# 3. Find its PID with `ps aux | grep bigfile`
# 4. Kill it with `kill -TERM <PID>`
Observe how the file size stops growing after the kill.
What’s Next?
- Signal handling in code – Learn how programs can catch
SIGTERMand clean up resources. - Using
systemdto manage long‑running services – Replace manualnohup/&with proper unit files. - Advanced
htopcustomizations – Columns, filters, and user‑defined meters. - Process accounting with
pidstatandperf– Dive deeper into performance analysis.
Keep this page bookmarked; you’ll return to it whenever a process misbehaves.