xv6, line by line
tour 47
Tours47 Where a suspended process lives

Tour 47 · The dance of privilege · about 28 minutes · 17 steps

Where a suspended process lives

A process that is not running is not anywhere in the CPU. No hart holds its registers, no pc points into its code. Yet some hart can pick it up at any moment and continue it as if nothing had happened. So everything needed to continue it must be in memory. Where, exactly?

The short answer is three places: the trapframe (the user registers), the kernel stack (the kernel’s call chain), and p->context (14 registers saved by swtch). The short answer is often followed by a tidy claim: the three never overlap, and together they hold every register exactly once. This tour checks that claim against real memory, dumped with gdb from this build while usertests preempt ran on three harts. It turns out to be half right, and the half that is wrong teaches the most: some register values are stored twice, sometimes as the same number and sometimes as different ones, and the design is correct anyway.

Then we follow the three places through a process’s life: what fork gives a child, what exec replaces, and what exit and wait free, and what they never free.

Best after: 7. The trampoline and the trapframe, 13. swtch and the lock handed across a context switch, 20. fork, 21. exit, wait and zombies, 22. exec, 45. One complete time slice on three harts

Who is running where

We stop the machine at three moments, from two runs. In the first two (usertests preempt), hart 2’s scheduler has just come back from a swtch. In the third (usertests writebig), it is hart 1’s. Either way, the process it left is fully suspended:

Process Slot Why it stopped Kernel stack used
pid 5, a spinning child proc[4] timer, in user mode 112 bytes
pid 4, the test proc[3] asleep in read on a pipe 336 bytes
pid 3, the shell’s child proc[2] timer, in the kernel, during exec 1920 bytes

Meanwhile the other harts are running other processes, and the scheduler stacks are in use. Each hart runs at most one process; the suspended ones are pure data.

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. 1Three places, filled by three mechanisms kernel/proc.h
  2. 2Place 1: the trapframe, 31 registers and a pc kernel/proc.h
  3. 3Who wrote each trapframe field, and when kernel/trampoline.S
  4. 4Place 2: the kernel stack, 112 bytes deep kernel/proc.c
  5. 5Place 3: p->context, 14 registers kernel/swtch.S
  6. 6Overlap, checked register by register kernel/proc.h
  7. 7A consequence of the calling convention kernel/trampoline.S
  8. 8Asleep in read: 336 bytes and a syscall's arguments kernel/pipe.c
  9. 9Preempted inside the kernel, with a kernelvec frame kernel/kernelvec.S
  10. 10Where the interrupted kernel pc is kept kernel/trap.c
  11. 11fork: a child with a forged context and an empty stack kernel/proc.c
  12. 12fork: the trapframe is copied, with one change kernel/proc.c
  13. 13fork: the child's first run, top of an empty stack kernel/proc.c
  14. 14exec: the user half is replaced, the kernel half is not kernel/exec.c
  15. 15exit: a zombie keeps its stack and its context kernel/proc.c
  16. 16wait: the parent frees two places of three kernel/proc.c
  17. 17The ledger of a suspended process kernel/proc.h

Keys: ← → step · Home start