When a machine is slow, unresponsive, or something is running that should not be, these are the tools that tell you what is actually happening. ps takes a snapshot, top and htop watch continuously.
ps: a snapshot
ps has two incompatible sets of options for historical reasons — BSD style without dashes and UNIX style with them. Two invocations cover almost everything:
ps aux # BSD style — everything, with CPU and memory
ps -ef # UNIX style — everything, with parent PIDsUse aux when you care about resource usage, -ef when you care about which process started which.
$ ps aux | head -3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168404 11928 ? Ss Aug21 0:14 /sbin/init
www 2 1.3 2.4 984320 98204 ? Sl 09:12 4:21 /usr/sbin/nginx| Column | Meaning |
|---|---|
PID | Process ID — what you pass to kill |
%CPU | CPU used, averaged over the process’s whole lifetime |
%MEM | Share of physical RAM |
VSZ | Virtual memory reserved — usually meaningless, see below |
RSS | Resident set size: physical RAM actually in use, in KB |
STAT | Process state — see the table below |
TIME | Total CPU time consumed, not elapsed time |
Watch out for %CPU here. It is a lifetime average, so a process that pinned a core for an hour yesterday and has been idle since will still show a high number. For what is happening now, use top.
Process states
| Code | State |
|---|---|
R | Running or runnable |
S | Sleeping, waiting for something — the normal state for most processes |
D | Uninterruptible sleep, almost always disk or network I/O |
Z | Zombie — finished, but its parent has not collected the exit status |
T | Stopped, usually by Ctrl+Z or a debugger |
Suffixes add detail: s is a session leader, l is multi-threaded, + is in the foreground, < is high priority, N is low priority.
D is the one that matters when things hang. A process in uninterruptible sleep cannot be killed, not even with -9, because it is inside a kernel call waiting on hardware. Several processes stuck in D usually means a failing disk or a dead network mount, not a software bug.
Zombies are almost never the problem. A zombie holds a process table entry and nothing else — no memory, no CPU. You cannot kill one; it is already dead. Fix the parent, or the zombies disappear when the parent exits and init reaps them.
Picking columns and sorting
# Top 10 memory consumers
ps -eo pid,user,rss,comm --sort=-rss | head -11
# Top 10 CPU consumers
ps -eo pid,user,pcpu,comm --sort=-pcpu | head -11
# With the full command line and start time
ps -eo pid,ppid,lstart,etime,cmd --sort=start_time
# Just one process, by name
ps -C nginx -o pid,rss,etime
# Process tree
ps -ejH
pstree -p-o lets you ask for exactly the columns you want, and --sort=-field ranks by them descending. This is far better than piping ps aux through sort and guessing at column numbers.
Finding a process by name
pgrep nginx # just the PIDs
pgrep -a nginx # PIDs with command lines
pgrep -u www-data # everything owned by a user
pidof nginx # similar, space-separatedpgrep is the modern replacement for ps aux | grep name, and it avoids the classic annoyance of grep matching its own process — covered in the grep page.
top: watching it live
top -o %MEM # start sorted by memory
top -u www-data # one user only
top -p 1234,5678 # specific PIDs
top -b -n 1 # batch mode: print once and exit, for scriptsOnce inside, single keypresses do the work:
| Key | Action |
|---|---|
M | Sort by memory |
P | Sort by CPU |
T | Sort by cumulative time |
c | Toggle the full command line |
1 | Show each CPU core separately |
u | Filter by user |
k | Kill a process by PID |
H | Show individual threads |
q | Quit |
Load average, properly explained
load average: 2.15, 1.87, 1.42Three numbers: the average over the last 1, 5 and 15 minutes. This is the most misread figure in Linux administration.
It is not a percentage. It counts processes that are running or waiting to run — and on Linux, unlike most Unixes, it also counts processes blocked in uninterruptible I/O.
Compare it against your core count, which nproc gives you. On an 8-core machine, a load of 4 is comfortable and a load of 8 means fully committed. On a single-core machine, 4 means everything is queueing badly.
The three numbers together tell you the direction. 5.0, 2.0, 1.0 is a spike that just started. 1.0, 2.0, 5.0 is a problem that is resolving. And because I/O counts, a machine with load 12 and almost no CPU usage is not short of CPU — it is waiting on a disk.
Why %CPU can exceed 100
In top, 100% means one core fully used. A multi-threaded process on an 8-core machine can legitimately show 780%. Press 1 to see the cores individually, or Shift+I to switch to Irix mode, where the figure is divided by the core count.
htop: the one worth installing
sudo apt install htop # Debian, Ubuntu
sudo dnf install htop # Fedora, RHEL, Rocky, Alma
sudo pacman -S htop # ArchSame information, far better presented: per-core meters, colour, mouse support, scrolling, and a tree view on F5. F3 searches, F4 filters, F9 sends a signal with a menu rather than requiring you to remember numbers.
The reason to still know top is that it is installed everywhere, including on a stripped-down container or a machine you have no permission to change.
Stopping a process
kill 1234 # polite: sends SIGTERM
kill -9 1234 # forceful: sends SIGKILL
kill -HUP 1234 # often means "reload your configuration"
pkill nginx # by name
pkill -u alice # everything owned by a user
killall firefox # by exact name| Signal | Number | Effect |
|---|---|---|
TERM | 15 | Asks the process to shut down cleanly. The default. |
KILL | 9 | Kernel destroys it immediately. Cannot be caught or ignored. |
HUP | 1 | Conventionally “reload config”, though it depends on the program. |
INT | 2 | What Ctrl+C sends. |
STOP / CONT | 19 / 18 | Pause and resume. |
Do not reach for -9 first. SIGKILL gives the process no chance to flush buffers, finish writing files, remove lock files or close database connections. Corrupted data and stale locks are the usual reward. Send a plain kill, wait a few seconds, and escalate only if it is genuinely stuck.
And if -9 does not work either, the process is in D state and no signal will help. That is a hardware or filesystem problem.
For anything managed by systemd, use systemctl stop rather than killing it directly — otherwise systemd may simply restart it. See systemctl.
Priorities: nice and renice
nice -n 19 ./backup.sh # start it at the lowest priority
renice -n 10 -p 1234 # lower an already-running process
sudo renice -n -5 -p 1234 # raise it — requires rootNiceness runs from -20 (greediest) to 19 (most generous), default 0. The name is the right way to remember it: a nicer process yields more readily. Only root can be less nice than the default.
This only affects CPU scheduling. A backup job saturating the disk will not be helped by nice — use ionice for that.
Gotchas
VSZ is not memory usage
Virtual size counts everything the process has mapped, including shared libraries, memory it reserved but never touched, and memory-mapped files. A JVM routinely shows tens of gigabytes of VSZ on a machine with 8GB of RAM. RSS is the number that matters, and even RSS double-counts shared libraries across processes — so adding up RSS for every process will exceed your actual usage.
High memory usage is usually fine
Linux uses free RAM for disk cache, so a healthy server shows almost no free memory. Look at the available column in free -h, not free. Cache is reclaimed instantly when something needs it. Empty RAM is wasted RAM.
The process you killed came back
Something is supervising it — systemd with Restart=always, a container runtime, or a process manager. Stop it at that level instead.
Killing a parent does not kill its children
Orphaned children are re-parented to init and keep running. Use pkill -P 1234 to target children, or kill -- -1234 to signal the whole process group (note the negative PID).
Quick reference
ps aux # everything, with resource usage
ps -ef # everything, with parent PIDs
ps -eo pid,rss,comm --sort=-rss | head # top memory users
pgrep -a nginx # find by name
pstree -p # process tree
top # live view (M memory, P cpu, 1 cores, q quit)
htop # better live view
kill 1234 # ask nicely
kill -9 1234 # last resort
pkill -u alice # by user
nproc # core count, for reading load average
free -h # memory: read "available"Related commands
- systemctl — the right way to stop and start anything service-managed.
- Disk space — when processes are stuck in
Dstate, storage is often why. - awk — extracting PIDs and totals from
psoutput. - grep — and why
pgrepis usually better for finding processes. lsof— which files and sockets a process has open.strace— what a hung process is actually asking the kernel to do.
