Linux: Process Management
On Linux, a running program is called a process. Managing processes — seeing what's running, how much resources it's using, and how to gracefully stop it — is a core server administration skill.
📋 Prerequisites: You should already know
- Lesson 3: Terminal Basics
1. What You'll Learn
- ps: view process list
- top/htop: real-time monitoring
- kill vs kill -9
- Background execution (& / nohup / disown)
- Process priority with nice
2. A Story of a Service That Won't Start
(1) The Pain Point: Port Already in Use
Xiaoming encountered this when starting a Node.js service:
node server.js
# Error: listen EADDRINUSE: address already in use :::3000
The port was occupied. He didn't know which process was using port 3000 or how to stop it.
(2) Locate and Free the Port
# Find the process using port 3000
ss -tlnp | grep 3000
# LISTEN 0 128 *:3000 *:* users:(("node",pid=1234,fd=20))
# Terminate the process
kill 1234
# Restart the service
node server.js
(3) The Benefit: Port Conflicts Resolved
Xiaoming learned to use ss or lsof to find processes occupying ports, and kill to send signals to terminate them. Now he resolves port conflicts within 30 seconds.
3. Knowledge Points
(1) ps — Process Snapshot
# Core usage
ps aux # Detailed info on all processes
ps aux | grep nginx # Filter specific processes
ps -ef # Standard format
# Common field meanings
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# alice 1234 0.3 1.2 123456 54321 ? Ssl 10:23 0:01 node server.js
# Custom output
ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head -10
| Field | Meaning |
|---|---|
| PID | Process ID |
| PPID | Parent process ID |
| %CPU | CPU usage percentage |
| %MEM | Memory usage percentage |
| VSZ | Virtual memory size (KB) |
| RSS | Physical memory size (KB) |
| STAT | Process state (R=running, S=sleeping, D=uninterruptible, Z=zombie, T=stopped) |
| START | Start time |
| TIME | Cumulative CPU time |
| COMMAND | Command name / command line |
(2) top/htop — Real-time Monitoring
top # Start top
top -o %MEM # Sort by memory
top -u alice # Show only specific user processes
top -bn1 | head -20 # One-shot output
# Hotkeys while top is running
# P Sort by CPU
# M Sort by memory
# k Kill process (prompts for PID)
# u Filter by user
# q Quit top
# htop is an enhanced version of top (nicer UI, mouse support)
htop
(3) kill — Send Signals
kill PID # Send SIGTERM (15), request termination
kill -2 PID # Send SIGINT (2), equivalent to Ctrl+C
kill -9 PID # Send SIGKILL (9), force termination
kill -1 PID # Send SIGHUP (1), reload config
kill -19 PID # Send SIGSTOP (19), pause process
kill -18 PID # Send SIGCONT (18), resume process
# Kill by name
pkill -f "node server.js" # Match command line
killall nginx # Kill all processes with the same name
| Signal | Number | Meaning |
|---|---|---|
| SIGHUP | 1 | Hangup (reload config) |
| SIGINT | 2 | Interrupt (Ctrl+C) |
| SIGQUIT | 3 | Quit (with core dump) |
| SIGKILL | 9 | Force kill (cannot be caught/ignored) |
| SIGTERM | 15 | Request termination (default) |
| SIGSTOP | 19 | Stop (cannot be caught/ignored) |
| SIGCONT | 18 | Continue execution |
Core Principle: Try
kill PID(SIGTERM) first. Only usekill -9 PID(SIGKILL) if that doesn't work. SIGKILL gives the process no chance to clean up, which may cause data loss.
kill -9 (SIGKILL) forcefully terminates a process with no cleanup opportunity — open files may be corrupted, database transactions interrupted, and temporary files left behind. Only use -9 when kill PID (SIGTERM) is unresponsive. Never use it as a shortcut.
(4) Background Execution
# & — Put in background
node server.js & # Returns terminal immediately after starting
> 💡 Tip: Appending `&` to a command runs it in the background, freeing the terminal immediately. However, `&` alone means the process will be terminated when the terminal session closes. To keep a process running after the terminal closes, combine with `nohup`: `nohup command &`.
sleep 30 & # Run sleep in background
# jobs/fg/bg — Manage background jobs
jobs # List background jobs
# [1]+ Running node server.js &
# [2]- Running sleep 30 &
fg %1 # Bring job 1 to foreground
bg %2 # Resume paused job 2 in background
# Ctrl+Z — Suspend current foreground job and put in background
# Ctrl+C — Terminate foreground job
# nohup — Keep running after terminal closes
nohup node server.js &
nohup python app.py > app.log 2>&1 &
> ℹ️ Note: `nohup` + `&` is the standard combination for persistent background commands — `nohup` ignores the SIGHUP signal (sent when the terminal closes), and `&` puts it in the background. Output defaults to `nohup.out`; it's recommended to specify a log file manually: `nohup command > app.log 2>&1 &`.
(5) ▶ Example: Viewing Processes
# View all processes
ps aux | head -10
# View processes for a specific user
ps -u alice -o pid,%cpu,%mem,comm
# Top 10 processes by CPU usage
ps aux --sort=-%cpu | head -11
# View process tree
ps auxf | head -20
Output:
TEXTUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 169344 13224 ? Ss 10:00 0:02 /sbin/init root 2 0.0 0.0 0 0 ? S 10:00 0:00 [kthreadd] alice 1234 0.3 1.2 123456 54321 ? Ssl 10:23 0:01 node server.js alice 2345 0.0 0.0 2345 567 pts/0 S+ 10:30 0:00 ps aux PID USER %CPU %MEM COMMAND 1234 alice 0.3 1.2 node 5678 bob 0.1 0.5 python3 USER PID %CPU %MEM COMMAND root 1 0.0 0.1 /sbin/init root 456 0.5 2.1 /usr/bin/containerd alice 1234 8.2 3.4 node server.js bob 2345 5.1 4.2 python train.py
(6) ▶ Example: Real-time Monitoring
# Start top
top
# Operations inside top
# P → Sort by CPU
# M → Sort by memory
# k → Enter PID, press Enter, enter signal number (15 or 9), press Enter
# q → Quit
# Non-interactive mode
top -bn1 | head -15
Output:
TEXTtop - 10:30:01 up 5:23, 2 users, load average: 0.15, 0.10, 0.05 Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie %Cpu(s): 2.3 us, 0.8 sy, 0.0 ni, 96.5 id, 0.2 wa, 0.0 hi, 0.2 si MiB Mem : 3936.0 total, 512.0 free, 2048.0 used, 1376.0 buff/cache
(7) ▶ Example: Terminating a Process
# Start a background process
sleep 1000 &
# Find it
ps aux | grep "sleep 1000"
# alice 5678 0.0 0.0 1234 567 pts/0 S 10:23 0:00 sleep 1000
# Graceful termination
kill 5678
# If that doesn't work
kill -9 5678
Output:
TEXT[1]+ Terminated sleep 1000 alice 5678 0.0 0.0 1234 567 pts/0 S 10:23 0:00 sleep 1000 [1]+ Killed sleep 1000
(8) ▶ Example: Background Execution in Practice
# Start a long-running task
nohup python train_model.py > train.log 2>&1 &
# View the log
tail -f train.log
# Even after closing the terminal, train_model.py continues running
# After re-logging in, ps aux will show it's still running
Output:
TEXT[1] 5678 [2026-07-07 10:23:01] Training epoch 1/100... [2026-07-07 10:23:15] Loss: 0.5234 [2026-07-07 10:23:30] Training epoch 2/100...
(9) ▶ Example: nice/renice Priority Adjustment
# nice range: -20 (highest priority) to 19 (lowest priority)
# Start with lower priority
nice -n 10 ./heavy-task
# Adjust priority of an existing process
renice -n 5 -p 1234
# View process nice values
ps -eo pid,nice,comm | head -10
(10) ▶ Comprehensive Example: Service Management
#!/bin/bash
# manage-server.sh - Manage a Node.js application
APP_NAME="server.js"
APP_DIR="/home/alice/app"
PID_FILE="/tmp/app.pid"
start() {
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
echo "App is already running (PID: $(cat $PID_FILE))"
exit 1
fi
cd "$APP_DIR"
nohup node "$APP_NAME" > app.log 2>&1 &
echo $! > "$PID_FILE"
echo "App started (PID: $!)"
}
stop() {
if [ ! -f "$PID_FILE" ]; then
echo "App is not running"
exit 1
fi
PID=$(cat "$PID_FILE")
echo "Stopping app (PID: $PID)..."
kill "$PID" 2>/dev/null
sleep 2
if kill -0 "$PID" 2>/dev/null; then
echo "Force killing..."
kill -9 "$PID" 2>/dev/null
fi
rm -f "$PID_FILE"
echo "Stopped"
}
status() {
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
echo "Running (PID: $(cat $PID_FILE))"
ps -p $(cat "$PID_FILE") -o pid,%cpu,%mem,etime,comm
else
echo "Not running"
fi
}
case "${1:-status}" in
start) start ;;
stop) stop ;;
restart) stop; start ;;
*) status ;;
esac
❓ FAQ
Q: Why is
kill -9unsafe? A:kill -9(SIGKILL) forces the process to terminate immediately without any cleanup — open files may be corrupted, database transactions interrupted, and temporary files left behind. Only use it whenkill PID(SIGTERM) is unresponsive.
Q: What is a Zombie process? A: A process that has terminated but whose parent hasn't read its exit status. It leaves a "zombie" entry in the process table (STAT=Z). Zombies themselves don't consume resources, but too many can exhaust the process table. Usually, you need to kill or restart the parent process to clean them up.
Q: What's the difference between nohup and disown? A:
nohupsets the process to ignore SIGHUP at startup (sent when the terminal closes).disownis a bash built-in that removes an already-started job from the shell's job table.nohup command &is the most common approach.
Q: How to limit a process's CPU usage? A: Use
cpulimit:cpulimit -l 50 -p PIDlimits it to 50% CPU. Or useniceto lower priority. In Docker containers, use--cpusto limit.
Q: How to find which process is using a specific port? A: Use
ss -tlnp | grep :PORT(recommended) orlsof -i :PORT(requires lsof) orfuser PORT/tcp.
📖 Summary
ps auxfor process snapshot,top/htopfor real-time monitoring- Signal priority: SIGTERM (15) graceful termination → SIGKILL (9) force kill
&for background,jobs/fg/bgfor job managementnohup command &keeps running after terminal closesnice -n 10 commandadjusts priority
📝 Exercises
- Basic (⭐): Use
ps aux --sort=-%cputo find the top 3 processes by CPU usage, then startsleep 300 &, view it withjobs, and terminate it withkill - Intermediate (⭐⭐): Use
nohupto start a long-running command, close the terminal, reopen it, and verify it's still running. Then usess -tlnpto view all listening ports and their associated processes - Challenge (⭐⭐⭐): Write a process management script that supports start/stop/status/restart commands, uses a PID file to track the process, and implements graceful termination (SIGTERM first, wait 3 seconds, then SIGKILL)