xv6, line by line
tour 3
Tours3 main: one hart builds the kernel, the others wait

Tour 3 · Boot and build · about 33 minutes · 19 steps

main: one hart builds the kernel, the others wait

Three harts have just arrived in main, in supervisor mode, each on its own boot stack, with paging and interrupts off (Tour 2: Power-on to main, on every hart at once). Line 13 splits them. Hart 0 goes off to build the kernel: the console, the page allocator, the kernel page table, the process table, the trap vectors, the interrupt controller, the buffer cache, the inode and file tables, the disk driver and the first process. Harts 1 and 2 sit in a two-line loop, watching one integer.

This tour follows hart 0 through that sequence, one call at a time, and keeps a running count of something new: locks. Almost every init function creates one or more spinlocks or sleep-locks for a shared table, at the moment that table comes into existence. By the end, the kernel has well over a hundred.

Then we watch the hand-off. Hart 0 sets started with a release store, harts 1 and 2 see it with acquire loads, and the ordering those two words guarantee is what main relies on to keep them from using half-built tables. They print a line (racing for the console’s lock), turn on paging, install their trap vectors, and all three harts enter scheduler and start competing for the first process.

Best after: 2. Power-on to main, on every hart at once

Who is running where

Hart What it is doing
0 Entering the if (cpuid() == 0) branch of main: it will initialize everything
1 Spinning in the else branch, loading started over and over
2 The same as hart 1

All three have interrupts off and paging off. No process exists yet.

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. 1The fork in the road kernel/main.c
  2. 2consoleinit: the first lock kernel/console.c
  3. 3printk: hart 0 takes its first lock kernel/printk.c
  4. 4kinit: 32,735 pages, 32,735 trips through kmem.lock kernel/kalloc.c
  5. 5kvminit and kvminithart: paging on, for hart 0 only kernel/vm.c
  6. 6procinit: 66 more locks kernel/proc.c
  7. 7trapinit and trapinithart: a global lock, a per-hart register kernel/trap.c
  8. 8plicinit and plicinithart: shared priorities, private enables kernel/plic.c
  9. 9binit: 30 buffers, 30 sleep-locks kernel/bio.c
  10. 10iinit and fileinit: the inode and file tables kernel/fs.c
  11. 11virtio_disk_init: the disk driver kernel/virtio_disk.c
  12. 12userinit: the first process, built under its own lock kernel/proc.c
  13. 13The release store kernel/main.c
  14. 14The acquire loads on harts 1 and 2 kernel/main.c
  15. 15Two harts, one console lock kernel/main.c
  16. 16Harts 1 and 2 turn on paging kernel/vm.c
  17. 17Per-hart trap vector and interrupt enables kernel/main.c
  18. 18Three schedulers, one runnable process kernel/proc.c
  19. 19What main built kernel/main.c

Keys: ← → step · Home start