This question tests whether you can separate the direct cost from the indirect one. The interviewer wants the cache story — not just "the CPU switches tasks."
The mental model: a context switch is the scheduler swapping one thread's execution state for another's. The direct work is small: save the current thread's registers on its kernel stack, restore the next thread's, and jump. The expensive part is what the switch does to the CPU's caches.
Walk through the mechanism. Every thread has its own kernel stack; the previous task_struct is deactivated from the runqueue and the next one activated. The hardware state saved is roughly: general-purpose registers (callee-saved), instruction pointer, stack pointer, and flags. On x86-64, swapcontext-style mechanics are handled by the kernel's __switch_to, which also swaps the thread-local storage pointer and, for a process switch, CR3 — the root of the page table. Switching CR3 invalidates the entire TLB (without PCID tags), so every subsequent memory access from the new process misses and must re-walk page tables.
Now price it. Direct cost: roughly 1–5 microseconds — register saves, scheduler bookkeeping. But the real bill is indirect: the new thread's working set was evicted from L1/L2/L3 by the previous thread, and the TLB is cold, so the first milliseconds of execution are cache-miss-bound. Benchmarks show a process context switch costing on the order of 1–3µs in raw terms, but the effective performance impact — resumed execution at reduced cache efficiency — is multiples of that, and it compounds when switches happen thousands of times per second. Thread switches within one process are cheaper: CR3 and page tables are shared, so TLB entries survive, and only register state plus cache warmth are lost.
Tradeoffs and edge cases worth naming:
- Voluntary vs involuntary: a thread blocking on I/O switches voluntarily; the tick or a higher-priority wakeup preempts involuntarily. The latter is what you see as sys time in
vmstator highcscounts. - System call ≠ context switch: a syscall crosses into the kernel but stays on the same thread; no scheduler state changes.
- Reducing switches is a real optimization: thread pools sized to cores, epoll instead of one-thread-per-connection, and busy polling where latency beats power.
- Measure it:
perf sched, lmbench's context-switch benchmark, andvmstat'scscolumn all tell the switch rate.
A strong closing line: a context switch is cheap register surgery hiding an expensive cache transplant — the direct cost is microseconds, but the damage to TLB and cache warmth is what makes it worth avoiding.