A Linux host is reported to be pegged at high CPU, but `ps -eo pid,pcpu,args --sort=-pcpu | head` and `top -b -n1` both show no process above a few percent. Why do these two tools disagree with what the machine is doing right now?
answer
- a percentage needs a denominator
- ps divides by the whole lifetime
- one sample cannot be a rate
- top -n1 has nothing to subtract from
- take the second block, not the first
basics
~20 sNeither reading covers the last few seconds. The %CPU that ps prints is CPU time divided by the process's entire lifetime, and top's first batch iteration has no earlier sample to subtract from, so both show lifetime averages. Take a second top sample.
solid answer
~50 sBoth numbers are averages over the wrong window. `ps` computes `%CPU` as accumulated CPU time divided by how long the process has been alive, so a worker that has run for a week and only started spinning two minutes ago is diluted almost to zero — and conversely a short-lived process that burned a core for its whole two-second life reports near 100% long after it stopped mattering. `top` normally does the right thing, because it diffs two samples of `/proc`, but with `-n1` there is no previous sample to diff against, so the first screen falls back to lifetime figures and the summary line reflects since-boot totals. The fix is to make the tool measure an interval: run `top -b -n2 -d1` and throw away the first block, or watch `top` interactively for a few refreshes. Only then are you reading "CPU used in the last second".
code
bash · 1 linetop -b -n2 -d1 -o %CPU | awk '/^top -/{block++} block==2'go deeper
Know that ps prints a snapshot and its %CPU is an average over the process's whole life, so a freshly spiking process can look quiet. Reach for a live view before drawing conclusions.
Be able to explain that a rate needs two counter readings, that top produces one by diffing samples, and that -n1 leaves it with nothing to diff — so ask for two iterations and read the second.
Demonstrate the habit of confirming a rate over a known interval before naming a culprit, and be ready to say what it means when interval sampling still shows nothing: short-lived processes, kernel threads, or interrupt time.
Own the reasoning that snapshot tools are the wrong instrument for chasing bursty or short-lived load, and be able to justify when a team should move from ad-hoc sampling to event-based process accounting.
## Percentages are always over some window A CPU percentage is meaningless without the interval it is measured over. Every one of these tools reads the same raw counters out of `/proc` — per-process CPU time in `/proc/<pid>/stat` and system-wide totals in `/proc/stat` — which are monotonically increasing tick counters. To turn a counter into a rate you must subtract two readings. The differences between the tools come entirely from *which* two readings they subtract. ## What ps computes `ps` takes one reading. For `%CPU` (field `pcpu`) it divides the process's accumulated CPU time by its elapsed wall-clock lifetime and expresses the ratio as a percentage. The manual page says exactly this, and it has two consequences worth stating in an interview: - **Old processes are diluted.** A database that has been up for 30 days and is right now burning a full core will report a small percentage, because one busy minute is invisible against 30 days of elapsed time. - **Young processes are inflated.** A build step that ran flat out for its entire three-second life shows close to 100% (or more on multiple cores) even though it is a rounding error in the machine's day. The consequence people find most surprising is that the `%CPU` column does not sum to anything meaningful. On an 8-core box the sum of `%CPU` across all processes can be far below 800 even while every core is saturated, because the busy processes are long-lived. ``` ps -eo pid,etimes,times,pcpu,args --sort=-times | head ``` Comparing `etimes` (elapsed seconds) with `times` (CPU seconds) makes the arithmetic visible: those two numbers are precisely what `%CPU` is the ratio of. ## What top computes, and what -n1 breaks `top` from procps-ng is built around repeated sampling. On each refresh it re-reads `/proc`, subtracts the previous reading, and divides by the delay, so its `%CPU` genuinely means "share of a CPU used since the last refresh". That is the number you want during an incident. Batch mode (`-b`) with a single iteration (`-n1`) removes the thing that makes it work. There is no previous sample, so the first output block cannot show a delta: the summary `%Cpu(s)` line reflects totals since boot, and the per-task percentages are lifetime figures of the same kind `ps` prints. A script that does `top -b -n1` to "see what is hot" is therefore reading history, not the present. The correct batch invocation asks for two iterations and discards the first: ``` top -b -n2 -d1 | awk '/^top -/{block++} block==2' ``` `-d1` sets a one-second delay, so the second block covers exactly that second. Other flags worth knowing: `-o %CPU` sets the sort field, `-H` shows individual threads rather than whole processes, `-p PID` restricts output to one process, `-c` toggles the full command line, and `-w` widens the output so long command lines are not clipped. ## The other reason the numbers look small: normalisation `top` defaults to what is historically called Irix mode, where `%CPU` is expressed relative to a single CPU — so a four-threaded process saturating four cores legitimately shows about 400%. Pressing `I` toggles Solaris mode, which divides by the number of CPUs and would report the same process as 100%. Confusing the two makes a busy process look either impossibly hot or oddly cool. `ps` does not offer that toggle; its ratio is single-CPU-relative too, which is why `%CPU` above 100 is possible there as well for a multithreaded process. ## How to answer this in practice The method is: never trust a single snapshot for a rate. Get an interval measurement — a second `top` sample, or `top` running interactively — and confirm it against the system-wide view before you name a process. If the interval measurement still shows no busy process, the CPU time is going somewhere the per-process view does not attribute cleanly: short-lived processes that are born and die between samples (a fork storm from a cron job or a health check), kernel threads, or interrupt and softirq time. Short-lived processes in particular are invisible to any sampling tool by construction — by the time the second sample is taken they are gone — and that is the point at which you stop sampling and reach for a tool that hooks process creation instead. ## What to say about the trap The interviewer is checking whether you know that a percentage has a denominator. If you can state that `ps` divides by lifetime and `top` divides by the interval between two samples — and that `-n1` leaves top with no interval — you have answered the question.
- You take two top samples and still see no process above a few percent while the CPU is busy. What are you missing?Most likely processes that are born and die between the samples — a fork storm from cron, a shell-heavy health check, or a build. Sampling tools cannot see them by construction. It can also be time that is not attributed to a task at all: kernel threads, interrupt and softirq handling. At that point you stop sampling processes and trace process creation or the system-wide CPU breakdown instead.
- Why can top show a single process at 380% CPU?Because top defaults to Irix mode, where the percentage is relative to one CPU, so a process running four threads on four cores legitimately reads about 400%. Pressing `I` switches to Solaris mode, which divides by the CPU count and would show the same process as roughly 95%. Neither is wrong; you just have to know which mode you are reading.
- If ps %CPU is a lifetime average, when is it actually the number you want?When you care about a process's total appetite rather than its current state — for example spotting a daemon that has quietly consumed hours of CPU since boot, or confirming that a supposedly idle agent has burned nothing. Comparing accumulated CPU time (`times`) against elapsed time (`etimes`) answers "has this thing ever been busy", which a one-second interval sample cannot.
saying these in an interview costs you the question
- Treats ps %CPU as current, instantaneous CPU usage
- Believes top -b -n1 shows the last second
- Expects %CPU columns to sum to total system utilisation
- Says a process over 100% must be a bug
- Concludes the CPU is idle from one snapshot