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

1. What You'll Learn


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:

BASH
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

BASH
# 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

BASH
# 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

TEXT
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

BASH
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 use kill -9 PID (SIGKILL) if that doesn't work. SIGKILL gives the process no chance to clean up, which may cause data loss.

⚠️ Warning: 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

TEXT
# & — 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

BASH
# 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:

TEXT
USER       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

TEXT
# 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:

TEXT
top - 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

BASH
# 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

BASH
# 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

TEXT
# 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

BASH
#!/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 -9 unsafe? 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 when kill 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: nohup sets the process to ignore SIGHUP at startup (sent when the terminal closes). disown is 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 PID limits it to 50% CPU. Or use nice to lower priority. In Docker containers, use --cpus to limit.

Q: How to find which process is using a specific port? A: Use ss -tlnp | grep :PORT (recommended) or lsof -i :PORT (requires lsof) or fuser PORT/tcp.


📖 Summary


📝 Exercises

  1. Basic (⭐): Use ps aux --sort=-%cpu to find the top 3 processes by CPU usage, then start sleep 300 &, view it with jobs, and terminate it with kill
  2. Intermediate (⭐⭐): Use nohup to start a long-running command, close the terminal, reopen it, and verify it's still running. Then use ss -tlnp to view all listening ports and their associated processes
  3. 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)
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏