Context Switching: What the OS Saves and What It Costs
What the OS saves when the CPU moves between processes or threads, the direct and hidden costs, and a C measurement: about 1.5 microseconds per switch against 0.1 for a plain system call.
A context switch is the kernel saving the running task's CPU state (registers, program counter, stack pointer, flags) into its process control block and loading another task's state, so tasks can share a core. Interviewers expect the cost difference: a thread switch skips the page table change, while a process switch changes the address space and can invalidate TLB entries.
A context switch saves one task's CPU state and loads another's so many tasks can share a core. The direct cost is small, around a microsecond or two in a measurement here, but the hidden cost of cold caches and TLB misses afterward is often larger. Switching between threads of one process is cheaper than between processes.
- During a context switch the kernel saves registers, the program counter, the stack pointer and flags into the task's process control block, called task_struct on Linux.
- Switching between threads of the same process skips the page table change because they share memory mappings, which makes a thread switch cheaper than a process switch.
- In a pipe ping-pong test pinned to one core in a virtualised sandbox, a context switch cost about 1.4 to 1.6 microseconds, against about 0.11 microseconds for a getppid system call.
- The hidden cost of a context switch is cold CPU caches and a cold TLB afterward, which is often larger than the direct cost of the switch itself.
- A system call does not cause a context switch by itself: it changes privilege mode into the kernel, and a switch happens only if the call blocks or the scheduler picks another task.
01Why switches happen
A CPU core runs one task at a time. To give the illusion of many programs running at once, the OS switches between them. A switch happens when a task's time slice ends (a timer interrupt), when it blocks waiting for I/O or a lock, when a higher-priority task becomes ready, or when it exits.
The switch itself is done by the kernel scheduler. It chooses the next task, then runs a short piece of low-level code that swaps the CPU over to it.
02What gets saved and restored
The state of a running task is mostly CPU registers: general-purpose registers, the program counter (the next instruction), the stack pointer, and flags. The kernel saves these into the task's process control block (PCB, or task_struct in Linux), then loads the saved values of the next task. When the first task runs again, it continues exactly where it stopped without knowing it was paused.
If the next task belongs to a different process, the kernel also switches the address space by changing the page table pointer. Floating-point and vector registers are saved too, often lazily, only if the task used them.
03Process switch versus thread switch
Switching between two threads of the same process skips the page table change, because they share the same memory mappings. Switching to a different process changes the address space. On many CPUs this invalidates cached address translations in the TLB (translation lookaside buffer), so the next memory accesses are slower until the TLB refills. Modern CPUs tag entries with an address-space id to avoid a full flush, which reduces but does not remove the cost.
This is one reason threads are called lightweight, and why a server with many threads often does better than the same server with many processes.
04Measuring it
Two processes bounce a single byte back and forth over pipes. Both are pinned to the same core, so every hop forces a switch.
sched_setaffinity(0, sizeof s, &s); // pin to CPU 0
if (fork() == 0) { // child: read, then write back
for (i = 0; i < N; i++) { read(p1[0], &c, 1); write(p2[1], &c, 1); }
_exit(0);
}
for (i = 0; i < N; i++) { write(p1[1], &c, 1); read(p2[0], &c, 1); }With N = 200,000 round trips, three runs gave 2.8 to 3.3 microseconds per round trip, so about 1.4 to 1.6 microseconds per switch (each round trip has two). A plain system call (getppid) in the same program cost about 0.11 microseconds. These numbers include the pipe system calls themselves, and they come from a virtualised sandbox, so a bare-metal machine will show different values. Take away the order of magnitude: a switch costs on the order of a microsecond, which is thousands of instructions' worth of time.
05The hidden cost: cold caches
After a switch, the CPU caches hold the previous task's data. The new task starts with cache misses on its own data and instructions, and a cold TLB. Its first moments run well below normal speed. This indirect cost depends on how much memory the task touches. A task with a small working set recovers quickly; one that walks gigabytes recovers slowly, if at all before the next switch.
That is why very short time slices hurt throughput. A scheduler that switched every few microseconds would spend most of its time on overhead. Typical slices are a few milliseconds, a compromise between responsiveness and efficiency.
06Reducing switches
Practical ways to cut switching cost: use thread pools, not a thread per request, so the number of runnable threads stays near the number of cores. Use event-driven I/O (epoll, async runtimes) so one thread serves many connections without blocking. Avoid lock contention, since a thread that blocks on a lock causes a switch. Pin latency-sensitive threads to cores when you need predictable timing.
On Linux, vmstat shows context switches per second (cs), and pidstat -w splits voluntary switches (the task blocked) from involuntary ones (preempted). A high involuntary count suggests more runnable tasks than cores.
07Common interview questions
Q: What is a context switch?
A: Saving the state of the running task and restoring another's so the CPU can switch between tasks.
Q: What is stored in the PCB?
A: Process state, program counter, registers, scheduling information, memory management information, open files and process id.
Q: Why is a thread switch cheaper than a process switch?
A: Threads share the address space, so no page table switch or TLB invalidation is needed.
Q: What is the hidden cost of a switch?
A: Cold CPU caches and TLB, so the new task runs slowly at first.
Q: Does a system call cause a context switch?
A: Not by itself. It changes the privilege mode into the kernel. A switch happens only if the call blocks or the scheduler picks another task.
Q: Voluntary versus involuntary switch?
A: Voluntary when the task blocks or yields. Involuntary when it is preempted by the scheduler.