← Back to Table of Contents

Appendix H — The Life of a Running Computer

Here is a question almost no architecture text answers directly: what actually happens when you press the power button, and what is the CPU doing right now while you read this — when “nothing” is running? It feels like the processor must need a constant stream of instructions to “stay on,” as if someone has to keep feeding it work or it stops. And what keeps the screen lit if the CPU is doing nothing?

This appendix answers that end to end. It ties together the fetch–decode–execute loop (Chapter 4), firmware and the OS (Chapter 6), interrupts (Chapter 13 touched them), and power/idle states (Appendix D) into one story: how a computer comes alive, stays responsive, and sleeps between your keystrokes. The punchline reshapes how you think about your training loop too — because a GPU job is, at bottom, a host CPU doing exactly this dance with the accelerator.

The one-sentence version: A computer is not a loop you must keep spinning — it is an interrupt-driven machine that spends most of its life halted and asleep, woken thousands of times a second by hardware interrupts (a timer, your keyboard, a network packet, a “GPU done” signal) to do a tiny burst of work and then go back to sleep.


The Mental Model to Unlearn

Two opposite intuitions are both wrong:

Two Wrong Models vs Reality
❌ "It stops when done"
Like the TinyCPU in Ch 4 that hits HALT and ends. Real systems never run out of program — there is always an OS to return to.
❌ "It frantically loops"
You might picture the CPU spinning in a busy loop checking everything. That would waste 100% of the power for nothing.
✅ "It sleeps & is woken"
The CPU halts (clock stopped, low power) and is woken by interrupts only when there is something to do. Idle ≈ asleep-but-instant-wake.

The program counter (Chapter 4) does always point at some next instruction — that part of your intuition is correct. But “having a next instruction” is not the same as “executing continuously.” When there is genuinely nothing to do, the CPU executes one special instruction — HLT — that says “stop fetching and freeze my clock until an interrupt arrives.” It then draws a trickle of power and does literally nothing until the world pokes it. We’ll build up to exactly how that works.


From Power Button to Desktop: The Boot Sequence

When power stabilizes, the CPU is electrically reset: its registers take defined values and the program counter is forced to a hard-wired address called the reset vector (on x86, physical 0xFFFFFFF0, right at the top of the address space; on ARM, an address defined by the SoC). Whatever code lives there runs first. That code is firmware, baked into a flash chip on the motherboard.

Power-On → Login (each box is code the CPU fetch-decode-executes)
Reset
PC → reset vector
Firmware
BIOS / UEFI · POST
Bootloader
GRUB / systemd-boot
Kernel
Linux / NT / XNU
init (PID 1)
systemd / launchd
Login / Desktop
Stage Who runs What it does
Reset CPU hardware Forces PC to the reset vector; caches/MMU off, single core awake
Firmware (BIOS/UEFI) Flash ROM code POST (power-on self-test), trains and initializes DRAM, enumerates devices (PCIe, USB, storage), then loads the next stage
Bootloader e.g. GRUB, systemd-boot, Windows Boot Manager Finds the OS kernel on disk, loads it into RAM, hands over control
Kernel init OS kernel Sets up page tables (Ch 10), the interrupt vector table, drivers, brings the other CPU cores online, starts the scheduler
init / PID 1 systemd (Linux), launchd (macOS), smss (Windows) Starts background services/daemons, the display server, the login screen

Three things to notice. First, booting is a relay race of programs, each loading and jumping to the next — all of it ordinary fetch–decode–execute. Second, DRAM doesn’t work until firmware trains it, so the very first code runs from ROM/SRAM. Third, by the end, a special process — the scheduler — is in charge of deciding what every core does from then on. The machine is now “on,” and it will stay on the same way forever after: by being interrupted.


Interrupts: How the Outside World Breaks In

An interrupt is a hardware signal that says “stop what you’re doing and run this handler.” It is the single most important idea in this appendix, and the reason a CPU does not need to poll the world in a loop.

The mechanism:

Interrupt Flow
Device asserts an IRQ (e.g. keyboard, timer, NIC, GPU "kernel done")
CPU finishes the current instruction, then saves PC + registers
Looks up the handler address in the Interrupt Vector Table (IDT on x86)
Runs the ISR (Interrupt Service Routine) — tiny, fast
Restores saved state and returns (iret) — or calls the scheduler

A dedicated chip — the interrupt controller (APIC on x86, GIC on ARM) — collects IRQ lines from dozens of devices and prioritizes them. Interrupts come in three flavors:

  • Hardware interrupts — asynchronous, from devices: the timer, keyboard, mouse, network card, disk/NVMe, USB, and — critically for us — the GPU signalling that a kernel finished.
  • Exceptions / faults — synchronous, from the CPU itself: a page fault (Ch 10), divide-by-zero, illegal instruction.
  • Software interrupts / system calls — a program deliberately traps into the kernel (syscall/svc) to ask for I/O, memory, etc.

The most important one is the timer interrupt — the OS’s heartbeat. The kernel programs a hardware timer to fire periodically (historically 100 Hz, often 250–1000 Hz on Linux via CONFIG_HZ; modern kernels can run tickless). Every tick is a chance for the OS to regain control and decide who runs next.

Interrupt-driven beats polling. Polling = “ask every device, in a loop, forever” → 100% CPU, 100% power, even when idle. Interrupt-driven = “sleep until a device raises its hand.” The exception: very high-throughput paths (NVMe, DPDK networking, spinlocks held briefly) sometimes poll on purpose because an interrupt’s ~1 µs entry cost is too slow when events arrive millions of times per second — the same throughput-vs-latency trade you saw in Chapter 11.


The Scheduler: Who Gets the Core

After boot, every core runs under the scheduler. The kernel keeps a run queue of threads that are ready to execute. On each timer tick (or when a thread blocks), the scheduler picks one and runs it for a time slice (a few milliseconds), then preempts it and picks another — preemptive multitasking. The illusion of “everything running at once” on 8 cores is really the scheduler rapidly rotating dozens of threads through those cores.

Switching from one thread to another is a context switch: save the current thread’s registers and PC into its kernel data structure (the PCB / task_struct), load the next thread’s, and resume. It costs ~1–5 µs (plus cache/TLB cold-start afterward — Ch 10).

So when you think “nothing is running,” reality is: a few dozen background services/daemons exist, but almost all of them are blocked, sleeping on an event (a timer, a socket, a keypress). They are not in the run queue. Which raises the question the whole appendix is building toward: if the run queue is empty, what does the core do?


What “Idle” Really Means: the Idle Loop, HLT & C-states

When the scheduler finds no ready thread, it dispatches a special built-in thread: the idle thread (one per core). Its entire job is to put the core to sleep. Conceptually the kernel’s idle path looks like this:

1
2
3
4
5
6
7
8
9
/* Simplified Linux-style per-core idle loop (cpu_idle_loop) */
while (1) {
    while (!need_resched()) {       /* nothing ready to run?           */
        /* pick the deepest safe C-state, then halt the core:          */
        asm volatile("hlt");        /* x86: stop clock until interrupt */
        /* ARM equivalent: asm volatile("wfi");  // Wait For Interrupt  */
    }
    schedule();                     /* an interrupt woke us & queued work */
}

The HLT instruction (x86) / WFI (ARM) stops the core’s clock. The core consumes almost no dynamic power (recall $P_{\text{dyn}} \approx \alpha C V^2 f$ from Appendix D — set the switching activity to zero and you stop burning energy) and simply waits. The next interrupt — even just the timer tick — wakes it: the clock restarts, the ISR runs, and if that made a thread ready, the scheduler dispatches it; otherwise it halts again.

Modern CPUs go far deeper than a simple halt, via C-states (CPU idle power states) chosen by the OS governor based on how long it expects to stay idle:

C-state Name What’s turned off Wake latency Relative power
C0 Active nothing — executing — 100%
C1 Halt core clock gated (HLT) ~1–5 ns ~30–50%
C3 Sleep L1/L2 flushed, PLLs off ~50–100 ns ~10–20%
C6 Deep power down core voltage ≈ 0, state saved to SRAM ~µs ~1–5%

Deeper states save more power but take longer to wake — a latency/efficiency trade-off the kernel manages continuously. And tickless (NO_HZ) kernels go one step further: if a core will be idle for a while, the kernel stops the periodic timer interrupt itself, so a deeply idle laptop core can sleep for tens of milliseconds uninterrupted — which is why your laptop battery lasts hours while “doing nothing.”

So: when no process is running, the CPU is not executing instructions in a loop — it is halted in a C-state, clock stopped, sipping milliwatts, waiting for the next interrupt. That is the precise answer to “what happens when it’s idle.”


Who Keeps the Screen Lit While the CPU Sleeps?

If the CPU is asleep most of the time, how does the monitor keep showing a stable image at 60 Hz? Answer: the CPU isn’t involved frame-to-frame at all.

A region of memory holds the framebuffer — one pixel value per screen pixel (e.g. 3840×2160 × 4 bytes ≈ 33 MB for a 4K display). A dedicated hardware block — the display controller (a.k.a. display engine / CRTC, part of the GPU or SoC) — autonomously reads that framebuffer over DMA and streams it pixel-by-pixel to the panel through its timing generator, 60–144 times per second, with zero CPU instructions per frame.

Display Scanout — All Hardware, No CPU Loop
Framebuffer in RAM
~33 MB @ 4K
Display controller
DMA + timing gen
Panel @ 60–144 Hz
continuous scanout

The CPU/GPU only do work when the image changes — to render a new frame and write it into the (back) framebuffer. On a static screen (this page, sitting still), the CPU is overwhelmingly in a deep C-state while the display engine quietly refreshes the unchanged pixels. A few related tricks:

  • Double buffering + VSync: the GPU draws into a back buffer and swaps it to the front at the vertical-blank (vblank) boundary, so you never see a half-drawn frame (tearing).
  • Panel Self-Refresh (PSR): on laptops, when the image is static, the panel refreshes itself from its own local memory so even the display engine and memory can idle — more battery saved.

This is the second half of the answer to your question: the computer “stays on and displays” because dedicated hardware, not the CPU, drives the screen.


Putting It Together: One Keystroke, End to End

Watch the whole machine breathe through a single event:

A Day in the Life: You Press a Key
① Idle: every core in C6 (clocks stopped). Display engine scans out the unchanged framebuffer at 60 Hz.
② You press a key → keyboard controller raises an IRQ.
③ A core wakes (C6 → C0 in ~µs), runs the keyboard ISR, queues a "key" event, marks the focused app's thread ready.
④ Scheduler dispatches the app thread; it computes the new character and writes pixels into the back framebuffer.
⑤ At the next vblank the display engine scans out the updated framebuffer — you see the letter appear.
⑥ Run queue empties again → the core executes HLT and drops back to sleep. Total active time: well under a millisecond.

A “busy” desktop is just this loop happening thousands of times a second — timer ticks, mouse moves, network packets, disk completions — each a brief wake-work-sleep cycle. The CPU is reactive, not continuous.


Why This Matters for Model Optimization

This exact pattern governs how your training and inference jobs actually run — because a GPU workload is a host CPU performing this wake/sleep dance with the accelerator.

The Host ↔ Device Loop Is the Same Story
The host loop launches, then sleeps
Your Python loop calls the driver to enqueue kernels on a CUDA stream, then often blocks on torch.cuda.synchronize() — the host thread is parked (idle/halted) until the GPU raises a completion interrupt. The CPU isn't "running the model"; it's dispatching and waiting.
A slow host loop starves the GPU
If the CPU can't enqueue the next kernel before the current one finishes, the GPU goes idle between kernels → low "GPU utilization." This is why kernel-launch overhead, Python overhead, CUDA Graphs, kernel fusion, and async DataLoader prefetch matter so much.
DataLoaders ride the scheduler
Multi-worker data loading uses OS processes/threads, interrupts (disk/network I/O completions), and the scheduler to overlap CPU data prep with GPU compute — the same interrupt-driven concurrency, applied to feeding the accelerator.
Idle hardware still costs money
An idle GPU between inference requests clock-gates but still draws baseline power. Underutilized accelerators waste $/token — the economic reason serving systems batch aggressively (Ch 29) to keep the device in C0, not idling.

When you profile a training step and see the GPU sitting at 60% utilization, you are watching this appendix in miniature: the device finishing work and idling while the host loop scrambles to feed it the next kernel. Understanding the wake/dispatch/sleep cycle is what lets you tell a compute bottleneck from a host-overhead bottleneck.


Key Takeaways

  • A computer is interrupt-driven, not loop-driven: it spends most of its life halted and is woken by hardware interrupts to do brief bursts of work.
  • Boot is a relay of programs — reset vector → firmware (BIOS/UEFI) → bootloader → kernel → init — each ordinary fetch–decode–execute, ending with the scheduler in charge.
  • Interrupts (timer, devices, the GPU’s “done” signal) and the interrupt vector table are how the CPU reacts to the world without polling; the timer interrupt is the OS’s heartbeat.
  • The scheduler rotates ready threads across cores via context switches; “idle” background services are blocked, not spinning.
  • Idle = HLT/WFI + C-states: the core stops its clock and parks in a low-power state (C1→C6) until an interrupt; tickless kernels let it sleep even longer. This is what happens when “nothing” is running.
  • The screen stays lit by dedicated hardware — the display controller DMA-streams the framebuffer to the panel at the refresh rate with no per-frame CPU work.
  • Your GPU jobs are the same dance: the host enqueues kernels and idles awaiting a completion interrupt — so host-loop overhead, CUDA Graphs, and batching directly determine whether the accelerator stays busy.

This appendix is the runtime counterpart to Chapter 4 — Anatomy of a Processor: Chapter 4 shows how one instruction executes; this shows how billions of them get orchestrated into a living, responsive machine.

← Back to Table of Contents