xv6, line by line
tour 12
Tours12 One scheduler per hart

Tour 12 · Time and scheduling · about 27 minutes · 16 steps

One scheduler per hart

xv6 has no central scheduler that hands out work. Instead every hart runs its own copy of the same loop, scheduler, forever, and all three loops walk the same table of 64 processes, proc[], looking for one marked RUNNABLE. Nobody coordinates them. Two harts may reach the same process at the same instant.

This tour watches three schedulers at work while a test program creates processes, and answers the questions that design raises: how two harts decide which of them gets a process (one acquire, one atomic instruction), why a process can never run on two harts at once, why the scheduler briefly turns interrupts on and immediately off again, what an idle hart does, and how fair the result is.

Tour 11: From a timer tick to a context switch showed a process leaving its CPU on a timer tick. This tour stays with the schedulers on the other side of that switch. Tour 13: swtch and the lock handed across a context switch looks inside the switch itself.

Best after: 11. From a timer tick to a context switch

Who is running where

The machine has three harts. You typed usertests preempt. The test process, pid 4 (in slot proc[3]), is about to fork its children; on a fresh boot they land in proc[4], proc[5] and proc[6] as pids 5, 6 and 7. When the tour starts:

Hart What it is doing
0 Running pid 4, inside the fork system call
1 In its scheduler, stopped at wfi: nothing was runnable on its last scan
2 Also in its scheduler at wfi

init (pid 1), the shell (pid 2) and usertests (pid 3) are SLEEPING in wait.

Three harts are running. This tour follows one path through the code, but the machine has three CPUs executing at the same time. Watch the locks held display at the top of each step, and read the Meanwhile, on other harts boxes: they show what the other CPUs could be doing at that very moment.
The route
  1. 1Every hart ends its boot in the same loop kernel/main.c
  2. 2A scheduler belongs to one hart forever kernel/proc.c
  3. 3The struct each hart fights over kernel/proc.h
  4. 4A new process becomes visible only when complete kernel/proc.c
  5. 5The idle hart sleeps with interrupts off kernel/proc.c
  6. 6Open the window, let the interrupt in kernel/proc.c
  7. 7A tick inside the scheduler does not yield kernel/trap.c
  8. 8Close the window before scanning kernel/proc.c
  9. 9The scan, one lock at a time kernel/proc.c
  10. 10Two harts reach proc[4] at once kernel/spinlock.c
  11. 11Hart 1 claims pid 5 and switches to it kernel/proc.c
  12. 12Hart 2 gets the lock, and sees RUNNING kernel/proc.c
  13. 13A scheduler comes back and keeps its place kernel/proc.c
  14. 14How fair is it? kernel/proc.c
  15. 15When the table is empty, the hart rests kernel/proc.c
  16. 16What three independent loops add up to kernel/proc.c

Keys: ← → step · Home start