top: The Live Screen
top is ps with a pulse. It repeats the
census every few seconds, sorts it by
appetite, and puts a five-line summary of
the whole machine on top. Everything in
this chapter is captured in batch
mode — top -b — which prints screens
as plain text instead of drawing them
for a terminal. Batch mode is the
capturable truth: what interactively is
a flickering display becomes, in batch,
a document you can save, diff, and quote
(chapter 9 builds on exactly that). One
batch-mode trap first, because it bites
everyone once: the first screen's
percentages are averages since the
processes started, not measurements of
the moment. Ask for two screens and read
the second:
$ top -b -n 2 -d 2 (first screen, header)
top - 11:09:08 up 18 min, 0 user, load average: 2.19, 1.86, 1.11
Tasks: 17 total, 3 running, 13 sleeping, 0 stopped, 1 zombie
%Cpu(s): 90.5 us, 9.5 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
The header, line by line
Line 1 is uptime wearing a watch:
current time, how long the machine has
been up, and the load averages of
chapter 2.
Line 2 — Tasks: the census in one
breath. 17 total, 3 running, 13
sleeping, 0 stopped, 1 zombie is the
state alphabet of chapter 3, counted.
On the lab machine: the two spinners
and one more were running; the zombie
is the specimen from chapter 4.
Line 3 — %Cpu(s): where the machine's CPU went during the interval, in percents of all cores combined:
- us — user: programs doing their own work. The spinners live here.
- sy — system: the kernel working on programs' behalf.
- ni — nice: user work running at lowered priority (chapter 8's lever, made visible).
- id — idle. The money column for "is the CPU the problem": 0.0 id with a queue means yes.
- wa — I/O wait: cores idle because the runnable work is waiting on I/O. The tell that separates "CPU-bound" from "disk-bound," and the subject of the capture below.
- hi / si — hardware and software interrupt time; usually small, named for completeness.
- st — steal: time this virtual
machine wanted a core and the
hypervisor gave it to a neighbour.
The build machine, a sandbox, shows
small nonzero
st(0.2–0.9 in these captures) — a tenant's tax you can measure but not appeal.
Lines 4–5 — Mem and Swap: the memory
headline, decoded properly in chapter 7.
For now: avail Mem is the column to
trust, and this machine's Swap line is
all zeros because it has no swap at all.
The table below the header
Same columns as ps, interval-flavoured: %CPU here is current appetite. Second screen of the same run, both spinners at work:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9428 root 20 0 15476 10464 6604 R 76.1 0.1 0:24.35 python3
9435 root 20 0 15476 10520 6660 R 71.6 0.1 0:24.13 python3
Two specimens, each alone on top, TIME+ accumulating about three-quarters of a second per second on this metered sandbox. Later, with one spinner killed and the other alone on the machine, the same column read 99.0 — effectively a whole core. Which brings up the arithmetic that confuses every top reader once:
100% in top means one full core. The
default (Irix) mode counts per core, so
a single-threaded process maxes at 100,
a busy multithreaded one can pass it —
up to cores × 100 (200 on this machine,
which is also the %Cpu(s) line's whole
budget). The spinner at 99% is not "the
machine at 99%"; it is one of two
cores spoken for, and the %Cpu(s)
line's id said so in the same breath:
44.7% idle. (Pressing I interactively
switches to Solaris mode, which divides
by the core count and makes 100% mean
the whole machine; the two modes cause
more cross-talk between colleagues than
any other top setting. Batch captures in
this guide are all Irix, the default.)
Sorting in batch is a flag, not a
keystroke: top -b -o %MEM re-sorts by
memory, and its second screen put the
known quantities in their known order —
a supervisor daemon and then the eater,
RES 211,664 KB, at the top. Interactively,
the keys P (CPU) and M (memory)
re-sort in place, x highlights the
sort column, and 1 splits the %Cpu(s)
line into one line per core — worth
pressing once on your own machine to see
your two (or sixty-four) cores as
separate citizens. Those keystrokes are
described here rather than captured;
batch mode has no keys to press, and
this guide doesn't dress descriptions up
as captures.
wa: the I/O tell, on camera
The single most useful diagnostic in the header is the one that answers "CPU-bound or disk-bound" without guessing. The build machine staged the answer: six small writers, each writing to disk synchronously — every write waited out by the kernel before the next could start — put six threads in state D:
$ ps -eo pid,stat,wchan:24,args | grep io_waiters
9927 Ds wait_log_commit python3 io_waiters.py
9928 Ds folio_wait_bit_common python3 io_waiters.py
9929 Ds wait_log_commit python3 io_waiters.py
(wchan names the kernel function each
is asleep inside — shown because ps can;
reading those names is beyond this
guide's scope, but their sameness is
the story.) And top, at the same
moment:
%Cpu(s): 53.0 us, 4.8 sy, 0.0 ni, 0.0 id, 41.1 wa, 0.0 hi, 1.0 si, 0.2 st
Read it as a sentence: no idle CPU at
all (id 0.0), only about half the
machine doing program work (us 53),
and 41.1% of all CPU capacity standing
idle with its hands in its pockets
waiting for the disk (wa). Those
waiting threads still count in the load
average (chapter 2's definition, kept),
which is why this machine's load can
say "busy" while its cores say
"waiting." When the verdict matters:
high us is a CPU story; high wa with
resident D states is an I/O story; the
remedies share almost nothing.