Every Linux server admin has been there: the server is slow, a process is unresponsive, memory is climbing — and you need to know what's happening right now. Process management is one of the most fundamental skills in Linux system administration, and yet it's easy to spend years using only ps aux | grep and kill -9 without understanding the deeper mechanics.
This guide covers everything from what a process actually is at the kernel level, to mastering ps, top, htop, signals, priority tuning, and real-world troubleshooting scenarios. Whether you manage a single VPS or dozens of production servers, this reference will become one you return to regularly.
Already comfortable with ps aux and top? Good — this guide skips the basics fast and spends most of its time on what happens when a process misbehaves: zombies, orphans, the OOM killer, and the exact commands to run when a server is unresponsive at 3 AM. For the fundamentals first, see Process Management on Linux.
1. Process Fundamentals
What Is a Process?
A process is a running instance of a program. When you execute /usr/sbin/nginx, the kernel loads the binary, allocates memory, assigns a Process ID (PID), and begins executing instructions. Every process has:
- PID — Process ID, unique integer assigned by the kernel
- PPID — Parent Process ID, the process that spawned this one
- UID / GID — The user and group identity running the process
- Memory space — Virtual address space isolated from other processes
- File descriptors — Open files, sockets, pipes
- Environment variables — Inherited from the parent
Process vs Thread
A process is an independent execution unit with its own memory space. A thread is a lighter execution unit that shares memory space with other threads in the same process. Nginx, for example, uses a multi-process model (master + workers), while MySQL uses both processes and threads internally. You'll see both when inspecting running systems.
Process States
Every process is in one of these states at any given moment:
| State | Code | Meaning |
|---|---|---|
| Running | R | Actively executing on a CPU, or ready to run |
| Sleeping (interruptible) | S | Waiting for an event (I/O, timer, signal) |
| Sleeping (uninterruptible) | D | Waiting for I/O — cannot be killed (kernel operation in progress) |
| Stopped | T | Paused via SIGSTOP or Ctrl+Z |
| Zombie | Z | Finished but parent hasn't read exit status yet |
| Dead | X | Being removed — you'll almost never see this |
The state code you see in ps output may have additional flags: s = session leader, l = multi-threaded, + = foreground process group, < = high priority, N = low priority.
Process Lifecycle
All processes in Linux descend from init (or systemd, PID 1) through four fundamental operations:
fork() → Creates a copy of the current process (child inherits parent's memory)
exec() → Replaces the process image with a new program binary
wait() → Parent waits for child to finish, collects exit status
exit() → Process terminates, resources freed
This fork-exec model is how every command you type in a shell launches a new process. The shell forks itself, the child calls exec() with your command, and the shell waits() until it finishes.
The /proc Filesystem
The /proc filesystem is a virtual filesystem that exposes kernel data structures as files. Every running process has a directory at /proc/PID/:
/proc/1234/
├── cmdline # Full command line (null-separated)
├── status # Human-readable status info
├── stat # Machine-readable stats (used by ps, top)
├── mem # Process memory
├── fd/ # Open file descriptors (symlinks)
├── net/ # Network stats
├── cgroup # Cgroup membership
├── environ # Environment variables
└── maps # Memory mappings
# Read a process's command line
cat /proc/1234/cmdline | tr '\0' ' '
# Check which cgroup a process belongs to
cat /proc/1234/cgroup
# List open file descriptors
ls -la /proc/1234/fd
Foreground vs Background Processes
# Run a command in the background
./long-running-script.sh &
# Suspend the current foreground process (Ctrl+Z sends SIGSTOP)
# Then move it to background
bg %1
# Bring a background job to foreground
fg %1
# List all background jobs in current shell
jobs
# List jobs with PIDs
jobs -l
2. ps — Process Snapshot
The ps command takes a snapshot of the current process state. Unlike top, it's not real-time — it's a point-in-time report, which makes it ideal for scripting and logging.
The Most Common Invocations
# BSD-style: all processes, user-oriented format
ps aux
# UNIX-style: all processes, full format with PPID
ps -ef
# Custom format, sorted by CPU usage
ps -eo pid,ppid,user,%cpu,%mem,vsz,rss,stat,cmd --sort=-%cpu
# Custom format, sorted by memory usage
ps -eo pid,ppid,user,%cpu,%mem,vsz,rss,stat,cmd --sort=-%mem
Understanding ps aux Output
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 167636 11320 ? Ss Mar15 0:08 /sbin/init
www-data 1234 1.2 2.4 450120 48932 ? S 09:15 0:42 nginx: worker
mysql 5678 0.8 8.1 1245600 166420 ? Sl Mar15 12:14 /usr/sbin/mysqld
| Column | Meaning |
|---|---|
USER | Effective user running the process |
PID | Process ID |
%CPU | CPU usage percentage (since process started, not instantaneous) |
%MEM | Percentage of physical RAM used |
VSZ | Virtual memory size in KB (includes everything: code, data, shared libs, mapped files) |
RSS | Resident Set Size in KB — actual physical RAM used right now |
TTY | Terminal associated with process (? = no terminal, daemon) |
STAT | Process state (see states above) |
START | When process started |
TIME | Cumulative CPU time consumed |
COMMAND | Command name and arguments |
VSZ vs RSS: VSZ looks huge but is misleading — a lot of virtual memory is shared libraries mapped into many processes. RSS is the real RAM footprint. When diagnosing memory pressure, focus on RSS.
10 Practical ps Examples
# 1. Find a specific process
ps aux | grep nginx
# 2. Show process tree (parent-child relationships)
ps --forest -e -o pid,ppid,user,cmd
# 3. All processes by a specific user
ps -u www-data
# 4. Top 10 CPU consumers
ps aux --sort=-%cpu | head -11
# 5. Top 10 memory consumers
ps aux --sort=-%mem | head -11
# 6. Show only PID and command (minimal)
ps -eo pid,cmd
# 7. Find processes listening (combine with lsof)
ps aux | grep "[n]ginx" # The [] trick prevents grep matching itself
# 8. Watch process count for a service
watch -n1 'ps aux | grep -c "[p]hp-fpm"'
# 9. Check full command line (not truncated)
ps -p 1234 -o pid,cmd --no-headers --cols 200
# 10. Show process group, session, and threads
ps -eo pid,pgid,sid,nlwp,cmd | head -20
3. top — Real-Time Monitor
While ps gives you a snapshot, top gives you a live feed of what's happening on your system. It's the first tool most admins reach for when a server is under stress.
The Header Section
top - 14:32:18 up 12 days, 3:41, 2 users, load average: 0.52, 0.48, 0.45
Tasks: 187 total, 1 running, 186 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.2 us, 1.1 sy, 0.0 ni, 95.1 id, 0.3 wa, 0.0 hi, 0.3 si, 0.0 st
MiB Mem : 31898.4 total, 18234.1 free, 8421.7 used, 5242.6 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 22145.8 avail Mem
Breaking down the CPU line:
us— User space (your applications)sy— System/kernel spaceni— User space processes with altered nice priorityid— Idle time (the higher, the better)wa— Waiting for I/O — if this is high, you have a disk or network bottleneckhi— Hardware interrupt handlingsi— Software interrupt handlingst— Steal time (CPU stolen by hypervisor — relevant on VMs)
Understanding Load Average
Load average represents the average number of processes in the run queue (running + waiting for CPU) over the last 1, 5, and 15 minutes.
- On a single-core system: Load of 1.0 means 100% utilization. Load of 2.0 means the CPU has a queue.
- On a multi-core system: Multiply the "safe" threshold by the number of CPU cores. On an 8-core server, load average of 8.0 means full utilization — not necessarily a problem.
- Rule of thumb: If the 15-minute load average consistently exceeds your CPU count, investigate.
# Check number of CPUs
nproc
grep -c ^processor /proc/cpuinfo
Interactive top Commands
| Key | Action |
|---|---|
P | Sort by CPU usage (default) |
M | Sort by memory usage |
T | Sort by cumulative CPU time |
1 | Toggle individual CPU core breakdown |
c | Toggle full command path |
k | Kill a process (prompts for PID and signal) |
r | Renice a process (change priority) |
u | Filter by username |
f | Fields manager — add/remove columns |
W | Save current settings to ~/.toprc |
d | Change refresh interval (default: 3 seconds) |
q | Quit |
top in Batch Mode (for Scripting)
# Single snapshot, no interactive UI
top -b -n 1
# Top 20 processes by CPU, saved to file
top -b -n 1 | head -27 > /tmp/top_snapshot.txt
# Run every 5 seconds, 12 iterations (1 minute of data)
top -b -n 12 -d 5 >> /var/log/top_monitor.log
4. htop — The Better Top
htop is an interactive process viewer that improves on top with color, mouse support, process tree view, and a more intuitive interface. It's not installed by default on most servers but is the first tool many admins install.
# Install htop
apt install htop # Debian/Ubuntu
yum install htop # RHEL/CentOS
dnf install htop # Fedora
htop Layout
The htop interface has three sections:
- Header: CPU bars (one per core), memory bar, swap bar, task count, load average, uptime
- Process list: Sortable, color-coded, with optional tree view
- Footer: Function key shortcuts
CPU bars use colors: blue = low priority, green = normal, red = kernel. Memory bars use green = used, blue = buffers, yellow = cache.
htop Keyboard Shortcuts
| Key | Action |
|---|---|
F2 | Setup — configure meters, display options, colors |
F3 | Search — find process by name |
F4 | Filter — show only matching processes |
F5 | Toggle tree view — show parent-child relationships |
F6 | Sort — choose sort column interactively |
F7 / F8 | Lower / raise process priority (nice) |
F9 | Kill — shows signal selection menu |
F10 | Quit |
Space | Tag a process (for group operations) |
U | Untag all processes |
H | Toggle display of user threads |
K | Toggle display of kernel threads |
t | Toggle tree view |
I | Invert sort order |
htop vs top Comparison
| Feature | top | htop |
|---|---|---|
| Color output | Limited | Full color |
| Mouse support | No | Yes |
| Process tree view | No (use ps --forest) | Built-in (F5) |
| Signal selection UI | Manual entry | Menu (F9) |
| Scroll horizontally | No | Yes |
| Filter by name | No (u=user only) | F4 text filter |
| Tag multiple processes | No | Space key |
| Always installed | Yes (default) | Requires install |
| Scriptable (batch mode) | Yes (-b flag) | Limited |
| Per-CPU breakdown | Press 1 | Default (bars) |
5. Signals and kill
Signals are software interrupts sent to processes. When a process receives a signal, it can handle it (run a custom handler), ignore it, or be terminated by the default behavior. Understanding signals is the difference between graceful shutdowns and data corruption.
Common Signals
| Signal | Number | Default Action | Catchable? | Common Use |
|---|---|---|---|---|
SIGHUP | 1 | Terminate | Yes | Reload config (nginx, sshd) |
SIGINT | 2 | Terminate | Yes | Ctrl+C — interrupt |
SIGQUIT | 3 | Core dump | Yes | Ctrl+\ — quit with core |
SIGKILL | 9 | Terminate | NO | Force kill — last resort |
SIGUSR1 | 10 | Terminate | Yes | Application-defined (nginx: reopen logs) |
SIGSEGV | 11 | Core dump | Yes | Segmentation fault (bug) |
SIGUSR2 | 12 | Terminate | Yes | Application-defined |
SIGTERM | 15 | Terminate | Yes | Graceful shutdown — default kill signal |
SIGCHLD | 17 | Ignore | Yes | Child process stopped/terminated |
SIGCONT | 18 | Continue | Yes | Resume a stopped process |
SIGSTOP | 19 | Stop | NO | Pause process — cannot be ignored |
SIGTSTP | 20 | Stop | Yes | Ctrl+Z — terminal stop (catchable) |
Using kill, killall, and pkill
# Send SIGTERM (graceful) to a PID — this is the default
kill 1234
# Force kill — uncatchable, immediate
kill -9 1234
kill -SIGKILL 1234
# Reload nginx config without downtime
kill -HUP $(cat /run/nginx.pid)
kill -1 1234
# Kill all processes named "nginx"
killall nginx
# Kill all processes named "php-fpm" belonging to a user
pkill -u www-data php-fpm
# Kill a process by exact name match
pkill -x nginx
# Send signal to process group
kill -TERM -1234 # Negative PID = process group
# Find PIDs by name (before killing)
pgrep nginx
pgrep -la nginx # With command line
pgrep -u www-data # By user
Why You Should NOT Default to kill -9
SIGKILL (signal 9) is uncatchable and immediate. The kernel forcibly terminates the process without giving it a chance to:
- Flush write buffers to disk (potential data corruption)
- Release locks (can leave database files locked)
- Clean up temporary files
- Send final notifications or log entries
- Close network connections gracefully
The correct escalation ladder is:
# Step 1: Ask nicely (SIGTERM — graceful shutdown)
kill -15 PID
sleep 5
# Step 2: Check if it's still running
kill -0 PID && echo "Still alive"
# Step 3: Force quit if necessary (SIGKILL — last resort)
kill -9 PID
For services managed by systemd, use systemctl stop instead of kill — systemd handles the escalation for you and knows the correct PID. See our systemd deep dive for the full restart and watchdog toolkit.
6. Process Priority: nice and renice
Linux uses a priority system to decide how much CPU time each process gets. The "nice" value ranges from -20 (highest priority — the process gets the most CPU time) to +19 (lowest priority — yields to everything else). The default nice value for new processes is 0.
# Start a command with lower priority (nice value 10)
nice -n 10 ./backup-script.sh
# Start a command with higher priority (only root)
nice -n -5 ./critical-service
# Change priority of a running process
renice -n 10 -p 1234
# Change priority for all processes by a user
renice -n 5 -u backupuser
# Check current nice values
ps -eo pid,ni,cmd | head -20
Real-world examples:
- Run a compression job (
gzip,tar) atnice 19so it doesn't compete with web traffic - Run database backup dumps at
nice 10 - Antivirus scans (ClamAV) at
nice 15on production servers - Video encoding, log analysis, large rsync transfers — all benefit from nice tuning
# Backup with low CPU priority AND low I/O priority
nice -n 19 ionice -c3 tar czf /backup/sites.tar.gz /var/www/
The ionice command works similarly but for disk I/O scheduling: class 3 = idle (only runs when disk is otherwise unused).
7. Background Processes and Job Control
Job control lets you manage multiple processes in a single terminal session. It's essential for running long tasks without blocking your shell.
# Run a command in the background
./long-script.sh &
[1] 4821 # Job number + PID
# Start a command, then pause it (Ctrl+Z sends SIGTSTP)
./another-script.sh
^Z
[2]+ Stopped ./another-script.sh
# Resume job 2 in the background
bg %2
# List all jobs
jobs -l
[1] 4821 Running ./long-script.sh &
[2] 4892 Running ./another-script.sh &
# Bring job 1 to foreground
fg %1
# Wait for a background job to finish
wait %1
# Disown a job (detach from shell — keeps running if shell closes)
disown -h %1
nohup — Survive Terminal Closure
# Run a command that survives SSH disconnect
nohup ./long-migration.sh > /tmp/migration.log 2>&1 &
# Check the log
tail -f /tmp/migration.log
When you disconnect from SSH, the shell sends SIGHUP to all its child processes. nohup makes the process ignore SIGHUP, so it keeps running. Output that would go to the terminal is redirected to nohup.out by default (redirect it yourself for clarity).
screen and tmux: The Better Solution
For long-running interactive sessions, use screen or tmux instead of nohup. They create persistent terminal sessions that survive disconnects and can be reattached from any SSH session.
# tmux — start a named session
tmux new -s migration
# Run your long command inside tmux
./migration.sh
# Detach from session (keep it running)
# Press: Ctrl+B, then D
# Later, reattach
tmux attach -t migration
# List sessions
tmux ls
8. Advanced Resource Monitoring Tools
Beyond top and ps, Linux provides specialized tools for deeper insight into specific resource types.
vmstat — Virtual Memory Statistics
# Sample every 1 second
vmstat 1
# Sample 10 times at 2-second intervals
vmstat 2 10
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 18234456 124832 5123456 0 0 2 18 312 589 3 1 95 1 0
r— Processes waiting for CPU (run queue)b— Processes in uninterruptible sleep (blocked on I/O)si / so— Swap in / swap out per second (non-zero = memory pressure)bi / bo— Block I/O reads/writes per secondwa— CPU wait for I/O (high = disk bottleneck)
iostat — Disk I/O Statistics
# Install: apt install sysstat
iostat -x 1
# Extended output for specific device
iostat -x sda 1
Key columns: %util (device utilization — near 100% means saturated), await (average wait time in ms), r/s and w/s (reads/writes per second).
pidstat — Per-Process Tracking
# Track specific PID every 1 second
pidstat -p 1234 1
# Track CPU + memory for all processes
pidstat -u -r 1
# Track I/O for a specific process
pidstat -d -p 1234 1
lsof — Open Files and Ports
# List all files opened by a process
lsof -p 1234
# Which process is using port 80?
lsof -i :80
# Which process is using port 443?
lsof -i :443
# All network connections
lsof -i
# Files opened by a specific user
lsof -u www-data
# Which process has a file open (useful before unmounting)
lsof /var/log/nginx/access.log
strace — System Call Tracing
# Trace system calls of a running process
strace -p 1234
# Trace with timestamps and output summary
strace -p 1234 -c
# Trace a new command execution
strace ls /var/www
# Trace only file-related calls
strace -e trace=file ls /var/www
strace is invaluable when a process is hanging and you need to know exactly what kernel operation it's stuck on. High output of futex calls often indicates lock contention.
9. Zombie and Orphan Processes
Zombie Processes
A zombie (defunct) process has finished executing but its entry remains in the process table because its parent hasn't called wait() to collect its exit status. Zombies use no CPU or memory — they're just a PID entry. They become a problem only if they accumulate in large numbers (exhausting the PID namespace).
# Find zombie processes
ps aux | grep Z
# Or with ps options
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/ { print }'
You cannot kill a zombie with kill -9 — it's already dead. To remove it, you need to deal with the parent:
# Find the parent of the zombie (PPID)
ps -o ppid= -p ZOMBIE_PID
# Send SIGCHLD to the parent (asks it to call wait())
kill -CHLD PARENT_PID
# If that doesn't work, kill the parent
kill PARENT_PID
If the parent is itself a critical process (like init or a service manager), the zombie will be cleaned up eventually. If you're writing code that forks child processes, always call wait() or use SIGCHLD handler.
Orphan Processes
An orphan process is one whose parent has exited before it. Linux automatically re-parents orphans to PID 1 (systemd/init), which then acts as their parent and calls wait() when they finish. This is normal and expected behavior — nohup processes and daemon processes are intentional orphans.
| Property | Zombie | Orphan |
|---|---|---|
| Is it running? | No — finished executing | Yes — actively running |
| Uses CPU? | No | Yes |
| Uses memory? | No (just PID table entry) | Yes |
| Has parent? | Yes (parent hasn't called wait) | No (re-parented to PID 1) |
| Problem? | Yes if many accumulate | Usually normal |
| Solution | Fix/kill parent | Usually none needed |
10. OOM Killer — When Linux Runs Out of Memory
When the system runs critically low on memory and cannot fulfill an allocation request, the Linux kernel's Out-Of-Memory (OOM) killer selects a process to terminate. It's a last resort that prevents the entire system from crashing — but it can kill unexpected processes.
Checking if OOM Killed Anything
# Check kernel logs for OOM events
dmesg | grep -i "oom\|killed\|out of memory"
# With timestamps
dmesg -T | grep -i oom
# Check system journal
journalctl -k | grep -i oom
# Recent OOM events only
journalctl -k --since "1 hour ago" | grep -i oom
# Example OOM log output:
[123456.789] Out of memory: Kill process 8421 (mysql) score 850 or sacrifice child
[123456.790] Killed process 8421 (mysql) total-vm:2048000kB, anon-rss:1536000kB
Understanding OOM Scores
# Check OOM score for a process (higher = more likely to be killed)
cat /proc/1234/oom_score
# Check OOM score adjustment (-1000 = never kill, +1000 = kill first)
cat /proc/1234/oom_score_adj
Protecting Critical Processes from OOM
# Protect a critical process (set adj to -1000 = never kill)
echo -1000 > /proc/$(pgrep sshd | head -1)/oom_score_adj
# Make it permanent for a service via systemd
# In the service unit file:
# [Service]
# OOMScoreAdjust=-500
# Or for a specific process you're launching:
# (protection lasts only for that process run)
bash -c 'echo -500 > /proc/$$/oom_score_adj; exec ./critical-service'
Common OOM prevention strategies:
- Set appropriate memory limits per service (cgroups)
- Configure MySQL/PostgreSQL buffer pools correctly for available RAM
- Enable swap as a safety net (not a performance fix)
- Monitor RSS growth with alerting before it becomes critical
- Tune
vm.overcommit_memoryandvm.swappinessin/etc/sysctl.conf
11. Practical Troubleshooting Scenarios
Scenario 1: "The Server Is Slow — What's Eating CPU?"
# Step 1: Get a quick top-level view
top
# Step 2: Sort by CPU, get top offenders
ps aux --sort=-%cpu | head -15
# Step 3: If it's a PHP/Python/Node process with no useful name, check cmdline
cat /proc/PID/cmdline | tr '\0' ' '
# Step 4: If multiple workers of the same service, find the parent
ps --forest -p PPID
# Step 5: Check if it's CPU-bound or I/O-wait-bound
vmstat 1 5 # High 'wa' = I/O bound, high 'us'/'sy' = CPU bound
Scenario 2: "Memory Keeps Growing — Find the Leak"
# Step 1: Find who is using the most RSS (real RAM)
ps aux --sort=-%mem | head -15
# Step 2: Track a specific PID over time
watch -n5 'cat /proc/PID/status | grep -E "VmRSS|VmSize"'
# Step 3: Check for swap activity
vmstat 1 | awk 'NR>2 {print $7, $8}' # si, so columns
# Step 4: Use pidstat for detailed per-process memory stats
pidstat -r -p PID 5
Scenario 3: "Process Won't Die — Escalate Signals"
# The proper escalation sequence
PID=1234
# Round 1: Ask nicely
kill -15 $PID
sleep 5
# Round 2: More insistent
kill -2 $PID
sleep 3
# Round 3: Nuclear option (always works, except D-state processes)
kill -9 $PID
# Special case: D-state (uninterruptible sleep) processes CANNOT be killed
# They're stuck in kernel — waiting for I/O (often NFS or hung disk)
# Solution: fix the underlying I/O issue, reboot if necessary
Scenario 4: "Background Job Died After SSH Disconnect"
# The problem: SSH disconnect sends SIGHUP to child processes
# Fix going forward: use nohup
nohup ./your-script.sh > /tmp/output.log 2>&1 &
# Or use tmux/screen (better)
tmux new -s myjob
./your-script.sh
# Ctrl+B, D to detach
# Check if job is still running after reconnect
ps aux | grep your-script.sh
tmux attach -t myjob
Scenario 5: "Find All Processes by a Specific User"
# List all processes by user
ps -u username
ps -u www-data aux
# Count processes by user
ps aux | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
# Kill all processes by a user (use with caution!)
pkill -u username
killall -u username
# Find what a user is running right now
ps -u username -o pid,cmd --no-headers
Quick Reference Cheat Sheet
Essential Commands
| Command | Purpose |
|---|---|
ps aux | All processes, BSD format |
ps -ef | All processes, UNIX format (shows PPID) |
ps aux --sort=-%cpu | head | Top CPU consumers |
ps --forest | Process tree |
top | Real-time process monitor |
htop | Interactive process monitor (color, mouse) |
kill PID | Graceful terminate (SIGTERM) |
kill -9 PID | Force kill (SIGKILL) — last resort |
kill -HUP PID | Reload config (SIGHUP) |
killall name | Kill all processes by name |
pkill -u user | Kill all processes by user |
pgrep -la name | Find PIDs by name |
nice -n 10 cmd | Start with low priority |
renice -n 10 -p PID | Change running process priority |
nohup cmd & | Run surviving SSH disconnect |
jobs -l | List background jobs with PIDs |
vmstat 1 | Memory and CPU stats every second |
iostat -x 1 | Disk I/O stats |
lsof -i :80 | Which process uses port 80 |
lsof -p PID | Files opened by process |
dmesg | grep oom | OOM kill history |
strace -p PID | Trace system calls of a running process |
Signal Quick Reference
| Signal | Number | Use Case |
|---|---|---|
| SIGTERM | 15 | Graceful shutdown (default kill) |
| SIGKILL | 9 | Force kill — cannot be caught or ignored |
| SIGHUP | 1 | Reload config (nginx: kill -HUP $(cat /run/nginx.pid)) |
| SIGINT | 2 | Interrupt (same as Ctrl+C) |
| SIGSTOP | 19 | Pause process — cannot be caught |
| SIGCONT | 18 | Resume a stopped process |
| SIGUSR1 | 10 | App-defined (nginx: reopen log files) |
Process Management and Panelica
Understanding these fundamentals becomes even more powerful when your panel surfaces this information automatically. Panelica's real-time monitoring dashboard shows CPU, memory, and process counts per site without you having to run ps aux | grep manually — and cgroup-enforced limits mean a runaway script gets contained before it can starve the rest of the server.
The monitoring layer tracks RSS, CPU time, and I/O without SSHing in first. When a process starts climbing toward its memory ceiling, the dashboard shows it building well before it reaches OOM-killer territory.
That said, knowing what's underneath — the signals, the /proc filesystem, the process states — makes you a dramatically more effective operator when something unusual happens and you need to dig.
Frequently Asked Questions
What is the difference between a zombie and an orphan process?
A zombie has already finished executing but still has an entry in the process table because its parent has not called wait() yet — it uses no CPU or memory, just a PID slot. An orphan is still actively running; its original parent exited and it was re-parented to PID 1, which is normal, expected behavior for daemons.
Why can't I kill a process with kill -9?
If the process is in uninterruptible sleep (D state in ps), it is stuck waiting on a kernel-level I/O operation and cannot respond to any signal, including SIGKILL. This usually points to a hung disk, NFS mount, or driver issue that needs to be fixed at the source rather than the process itself.
How do I know if the OOM killer terminated a process?
Run dmesg | grep -i oom or journalctl -k | grep -i oom. The log entry names the killed process and its memory footprint at the time, which is usually enough to identify the culprit and adjust its oom_score_adj or memory limits going forward.
What is a safe nice value for a backup job?
Nice 15 to 19 is typical for backup, compression, and antivirus scan jobs — high enough that the process yields CPU to anything with default priority, low enough that it still makes steady progress. Combine it with ionice -c3 to also deprioritize disk I/O.
Conclusion
Process management is one of those topics where there's always more depth to explore, but the tools covered here — ps, top, htop, signals, nice/renice, job control, and the supporting cast of vmstat, lsof, and strace — cover the vast majority of real-world server administration scenarios.
The most important habits to build:
- Use
SIGTERMfirst, escalate toSIGKILLonly when necessary - Always
ps --forestor htop tree view before killing a process — you may need to kill the parent - Use
tmuxorscreenfor anything long-running over SSH - Check
dmesg | grep oomwhen a service mysteriously disappears - Set
nicevalues on resource-intensive background tasks from the start
Bookmark this page, reference the cheat sheet the next time a process goes sideways, and you'll have it handled in minutes rather than hours.