CPU: Reading Load, Utilization & Run Queues
System Monitoring & Performance Diagnosis
Chapter 2 · CPU: Reading Load, Utilization & Run Queues
Chapter 1 left one number deliberately unexplained: a load average of 8.42 in that chapter's top snapshot. This chapter explains exactly what load average measures (and what it surprisingly includes), why it means nothing at all without knowing the machine's core count, and how to read the rest of the CPU line — including two fields, wa and st, that can mean "slow" isn't really a CPU problem at all.
Load Average vs. CPU Utilization: Two Different Numbers
CPU utilization (the percentage figures in top's %Cpu(s) line) describes how busy the CPU is right now, over a very short interval. Load average is a different measurement entirely: a running average, over 1, 5, and 15 minutes, of the number of processes that were either using the CPU or waiting for something.
What "Normal" Load Actually Depends On: Core Count
A load average of 8 means something completely different on a 4-core machine than on a 16-core one — the raw number is meaningless without knowing how many cores are actually available to share the work.
| Load average ÷ core count | What it suggests |
|---|---|
| Well under 1.0 | Comfortably idle — plenty of spare capacity |
| Around 1.0 | Fully utilized, but not yet queueing — every core is busy, nothing is waiting |
| Well over 1.0 | Genuinely overloaded — more work wants to run than the machine can currently handle, and some of it is waiting |
Chapter 1's own example machine has 4 cores. A load average of 8.42 divided by 4 is just over 2.1 per core — meaning, on average, roughly twice as much work wants to run as the machine can actually handle at once. That's a genuine, meaningful overload signal, not a false alarm.
The %Cpu(s) Line In Depth
Chapter 1's snapshot also showed: 87.3 us, 9.1 sy, 0.0 ni, 2.1 id, 0.0 wa, 0.0 hi, 1.5 si, 0.0 st. Each field means something specific:
| Field | What it measures |
|---|---|
| us | Time spent running normal application ("user-space") code |
| sy | Time spent in the kernel — system calls, scheduling, and similar overhead |
| ni | User-space time specifically from processes running at a lowered ("niced") priority |
| id | Genuinely idle — nothing to do |
| wa | iowait — the CPU is idle, but specifically because it's waiting on a disk (or other I/O) operation to finish, not because there's no work |
| hi / si | Hardware and software interrupt handling |
| st | Steal time — on a virtual machine, time this VM wanted to run but the hypervisor gave the physical CPU to a different tenant instead |
In Chapter 1's snapshot, wa and st are both 0.0, and us alone accounts for 87.3% — confirming this genuinely is a CPU-bound problem, not a disk bottleneck wearing a CPU-shaped disguise, and not a cloud tenant losing CPU time to a noisy neighbor.
wa figure, the CPU itself usually isn't the actual bottleneck — it's sitting idle, waiting on disk. Treating that as a CPU problem and looking for a runaway process will come up empty; the real evidence lives in disk I/O metrics instead.
st figure means the physical hardware underneath this machine is genuinely oversubscribed by the hosting provider — no amount of tuning inside this particular system will fix it, because the CPU time is being taken away one layer below where this system has any control. Recognizing steal time early avoids a long, fruitless search through this machine's own processes for a cause that isn't there.
Run Queue: Processes Actually Waiting for a Turn
vmstat shows a more direct number: the r column, the count of processes literally waiting for a CPU core right now — a narrower, more specific signal than load average's broader, I/O-inclusive definition.
An r value consistently well above the core count (6–8, on a 4-core machine) across several samples confirms the same story load average already suggested: genuine, sustained CPU contention, not a brief one-off spike.
vmstat's r column — the number of threads actually waiting for CPU time — but there's no built-in figure that mirrors Linux's 1/5/15-minute averaged load number.
Working Example: Fully Reading Chapter 1's Snapshot
Put together: a load average of 8.42 on a 4-core machine is roughly 2.1 per core — genuinely overloaded. The %Cpu(s) line's 0.0 wa and 0.0 st rule out both disk I/O and hypervisor steal time as explanations. 87.3% us confirms the time is being spent running actual application code, not the kernel or interrupt handling. A follow-up vmstat check showing r consistently at 6–8 confirms this isn't a passing blip. Every piece of evidence agrees: this machine has a genuine CPU bottleneck, and the next step is identifying which specific process is responsible — Chapter 8's own territory.
Hands-On Exercises
Explain why a load average of 8 could be either a genuine problem or completely normal, depending on one specific piece of information this chapter says is required to interpret it.
📄 View solutionA machine shows a high load average alongside a high wa figure in top. Explain why this chapter says that's misleading to treat as a CPU problem, and where the real evidence would actually be found.
Explain what "steal time" specifically measures, and why this chapter says it can't be fixed by anything done inside the affected virtual machine itself.
📄 View solutionChapter 2 Quick Reference
- Load average includes processes waiting on I/O, not just CPU-hungry ones — it can reflect a disk problem, not just a CPU one
- Always divide load average by core count (
nproc) before judging it — the raw number alone is meaningless %Cpu(s)breakdown: us (app code), sy (kernel), wa (iowait — a disk story, not CPU), st (steal time — a hypervisor-level cause you can't fix locally)vmstat's r column is a more direct, narrower measure of processes actually waiting for a CPU core right now- Windows has no direct load-average equivalent — CPU utilization % and Processor Queue Length are the closest counterparts
- Next chapter: Memory: Used, Free, Cached & the "Available" Metric