Reading — step 1 of 5
Read
Context Switch
The context switch is the mechanism that makes every scheduling policy possible: stop task A mid-thought, resume task B exactly where it was stopped, and leave both convinced nothing happened. It is also a tax — paid on every single switch, thousands of times per second — and this lesson (plus your exercise) is about knowing exactly what it costs and why.
What actually happens
At the moment of a switch, the kernel is running (we got here via interrupt or syscall — ring 0). The core move, in order:
- Save A's CPU state into A's process control block: general-purpose registers, stack pointer, program counter, flags. After this, "task A" exists purely as data.
- Switch address spaces: load B's page-table root into
CR3. One instruction — with expensive echoes (below). Skipped entirely if A and B are threads of the same process. - Switch kernel stacks: every task has its own kernel stack; the kernel now stands on B's.
- Restore B's saved state from B's PCB and return. B resumes at the exact instruction where it was frozen — to B, the intervening milliseconds simply never occurred.
The core of step 1/4 in a real kernel is startlingly small — a few dozen assembly instructions. If that were the whole cost, switches would be nearly free. It isn't.
The two costs
Direct cost — the visible work: saving/restoring registers, updating kernel structures, running the scheduler's pick-next logic. On modern hardware this is roughly 1–2 microseconds. Small, bounded, easy to measure.
Indirect cost — the crime scene left behind:
- TLB flush. Writing
CR3invalidates the cached virtual→physical translations from the paging lesson. B starts with a cold TLB and pays a page-table walk on its first touches of memory. (Hardware mitigates with PCID/ASID tags — TLB entries stamped with an address-space ID so they can coexist — but misses still spike after a switch.) - Cache pollution. A spent milliseconds pulling its working set into L1/L2/L3. B's memory now has to evict it, miss by miss. A single L3 miss costs ~100ns; refilling a working set is tens of thousands of misses. The indirect cost routinely dwarfs the direct cost by 10–100× — this is why "just context switch more" is never a free answer, and why the quantum tradeoff from last lesson exists at all.
One corollary worth internalizing: thread switches within a process are cheap — same address space means no CR3 write, no TLB flush, and largely warm caches. The expensive switch is the cross-process one.
Your exercise: put numbers on it
The exercise models exactly this cost structure: a BASE_NS direct cost per switch, plus per-switch penalties (the cache/TLB refill component), summed over a sequence of switches. It looks like accounting — it is accounting, and it's the same accounting a kernel engineer does when deciding whether a design that doubles switch frequency is affordable. When your numbers show the penalty term dominating the base term, you've rediscovered the central fact of this lesson from your own output.
Voluntary vs involuntary
Two flavors, same mechanism, different tempo:
- Voluntary: the task blocked (I/O, lock, sleep). It wants off the CPU; the state save happens on a clean syscall boundary.
- Involuntary: the timer fired mid-computation and the scheduler evicted it. The task is frozen between two arbitrary instructions — which is precisely why every register must be saved perfectly. There is no "safe point"; every point must be safe.
vmstat shows your machine's switch rate as cs — a normal desktop idles at a few thousand per second and spikes to hundreds of thousands under load. Multiply by the full (direct + indirect) cost you computed in the exercise and you'll know, in percent, what the illusion of parallelism actually charges.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…