Skip to content
Lesson 10 of 24

Step 1 of 5 · Reading · ~3 min

Read

Processes & Scheduling

Context Switch

Switching from one process to another looks like magic: one function call returns into a different program. It is actually a few stores and loads on a stack. In this lesson your program is the CPU executing xv6's swtch routine on simulated kernel stacks. You push and pop real 32-bit words, and at the end eip and esp belong to a different process than when you started.

What a "context" is

The state that makes a thread of execution resumable is small: its registers and its stack. If the stack holds everything else, the saved context only has to hold the registers that the calling convention promises survive a function call. The x86 32-bit convention splits them:

KindRegistersWho preserves them
caller-savedeax, ecx, edxthe caller, before the call, if it needs them
callee-savedebx, esi, edi, ebpthe callee, which must restore them before returning

swtch is called as an ordinary C function, so the compiler has already dealt with the caller-saved registers around the call. All swtch has to save is the callee-saved four plus the return address (eip). xv6's struct context is exactly those five words, lowest address first: edi, esi, ebx, ebp, eip.

The trick: change the stack, then return

The routine does four things:

  1. The call instruction has already pushed the return address. This is the saved eip.
  2. Push ebp, ebx, esi, edi, in that order. The stack now holds a complete five-word context, and edi is at the lowest address.
  3. Store the current esp into the old process's context pointer, then load esp with the new process's saved context pointer. From this instruction on you are running on the other process's kernel stack.
  4. Pop edi, esi, ebx, ebp, then execute ret, which pops eip. The CPU continues where the new process last called swtch.

The two halves are mirror images, so every process that has ever been switched out has the same shape on its stack. That symmetry is what makes a brand-new process work: the kernel builds a fake context on its fresh stack, four zero words and an eip pointing at the function that should start running (xv6 points it at forkret). The first time the scheduler switches to it, ret "returns" into that function.

A worked trace

BOOT sched 8000 (the scheduler runs with esp = 0x8000), PROC a 4000 1000 (a fresh context at 0x4000 - 20 = 0x3FEC whose last word is 0x1000), then the scheduler sets ebx = 0x11 and switches to a from the return address 0x7777:

StepEffect
callesp = 0x7FFC, word 0x7777 stored there
push ebp, ebx, esi, ediwords at 0x7FF8, 0x7FF4 (holds 0x11), 0x7FF0, 0x7FEC
savesched's context pointer = 0x7FEC
loadesp = 0x3FEC
pops, retedi, esi, ebx, ebp = 0, eip = 0x1000, esp = 0x4000

Later a switches back; the scheduler's ebx = 0x11 comes back out of the words at 0x7FF4, untouched by anything a did.

What this does not include

Switching to a process with a different address space also loads CR3, and the CPU flushes the TLB entries of the old space. That is invisible here but dominates real cost: the register work is tens of nanoseconds, the refill of a cold TLB and cache is microseconds. It is why switching between threads of one process is cheaper than between processes.

Common mistakes

  • Saving the caller-saved registers too. The compiler already did, and the context would grow for nothing.
  • Pushing in the wrong order. Pop order must mirror it exactly, or ebx and esi swap.
  • Forgetting that ret pops eip, leaving esp four bytes off.
  • Updating the CPU registers but not recording the old context pointer, so the process can never be resumed.

Your exercise

The starter keeps the processes, memory and registers, and prints results. You write swtch, the push, save, load, pop, return sequence.

Up nextMLFQ & Priority SchedulingProcesses & Scheduling

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…