xv6, line by line
tour 45
Tours45 One complete time slice on three harts

Tour 45 · The dance of privilege · about 34 minutes · 19 steps

One complete time slice on three harts

This tour follows one complete time slice on one hart, from start to finish, and at every step it answers the master question (Mode, stack and page table: the master question): which mode, which stack, which page table? It also asks which process, and which locks.

The plot has four acts. Process A is preempted by the timer. The scheduler picks process B. B returns to user mode, makes system calls, and finally makes one that sleeps. The scheduler then resumes A, which returns to user mode exactly where it was interrupted. Every one of the eight transitions of Tour 41: Every transition: mode, stack and page table shows up except boot (T8), user exceptions (T2) and device interrupts taken in user mode (T3), and we count each one literally.

The story is not invented. We ran usertests preempt on three harts in a copy of the kernel that logs every trap, swtch and sleep into a memory buffer, and this exact sequence happened on hart 2. The stack addresses come from gdb on the unmodified kernel of this build. Tour 11: From a timer tick to a context switch followed the code of a preemption, and Tour 13: swtch and the lock handed across a context switch the lock handed across swtch. Here the code is familiar, and the point is the state: we build a grand state table one row per step, and at the end we read it whole.

Best after: 5. Life of a system call, 11. From a timer tick to a context switch, 13. swtch and the lock handed across a context switch, 16. sleep and wakeup, and the lost-wakeup problem, 41. Every transition: mode, stack and page table

Who is running where

usertests preempt (user/usertests.c:856) has forked three children that spin forever and a pipe that the third child wrote one byte into. In our run, when the tour starts:

Hart What it is doing
0 Running pid 7 in user mode: it wrote "x" into the pipe, closed it, and now spins
1 Running pid 6 in user mode, spinning
2 Running pid 5 in user mode, spinning. Pid 5 is A.

Pid 4 is B. It is the test itself, in slot proc[3]. It was asleep in read on the empty pipe; pid 7’s write woke it, so it is RUNNABLE, frozen inside sleep. All three harts are busy, so B can run only when some hart’s timer frees one. usertests (pid 3), the shell (pid 2) and init (pid 1) are asleep in wait.

Slots and stacks in this build: A is proc[4] with kernel stack KSTACK(4) at 0x3fffff5000–0x3fffff6000; B is proc[3] with KSTACK(3) at 0x3fffff7000–0x3fffff8000. Hart 2’s scheduler stack is its slice of stack0, 0x80009890–0x8000a890.

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. 1A spins, and hart 2 is entirely A's user/usertests.c
  2. 2T4: the tick arrives, and only three things change kernel/trampoline.S
  3. 3uservec saves A's registers, and loads a stack it cannot use yet kernel/trampoline.S
  4. 4usertrap on an empty kernel stack kernel/trap.c
  5. 5devintr says "timer", and A decides to yield kernel/trap.c
  6. 6yield takes A's lock and sched checks the rules kernel/proc.c
  7. 7T6, swtch #1: from A's stack to hart 2's scheduler stack kernel/swtch.S
  8. 8The scheduler lets go of A and scans on kernel/proc.c
  9. 9The scheduler picks B kernel/proc.c
  10. 10T6, swtch #2: onto B's kernel stack, deep inside a read kernel/proc.c
  11. 11B's way out, prepared with interrupts off kernel/trap.c
  12. 12T5: userret, and the moment with no stack kernel/trampoline.S
  13. 13ld sp, sret, and B is in user mode kernel/trampoline.S
  14. 14B's twenty quick system calls user/usertests.c
  15. 15wait sleeps, and swtch kernel/proc.c
  16. 16T6, swtch #4: the scheduler resumes A, where it left off kernel/proc.c
  17. 17A wakes up inside sched, as if nothing happened kernel/proc.c
  18. 18T5 again: A runs its jump kernel/trap.c
  19. 19The grand state table, read whole user/usertests.c

Keys: ← → step · Home start