xv6, line by line
tour 4
Tours4 From the first process to the shell prompt

Tour 4 · Boot and build · about 46 minutes · 27 steps

From the first process to the shell prompt

At the end of main, the kernel is built but nothing is running. There is no init program in memory, no shell, no user code at all: just one entry in the process table, marked RUNNABLE, with an empty address space. Less than a second later, the console shows

init: starting sh
$

and two processes are asleep, waiting for you.

This tour follows that second: userinit creating process 1, three schedulers racing to run it, forkret doing the file-system setup that main could not do, kexec loading /init from the disk image built in Tour 1: From make qemu to a disk image and a kernel, the first drop into user mode, init creating the console device and forking the shell, and the shell printing its prompt and blocking in read.

It is also the first time a process moves between harts. Whenever it waits for the disk, or later, once it runs with interrupts on, whenever a timer tick makes it yield, the hart it was on goes off to do something else, and whichever hart’s scheduler finds it runnable again picks it up (often the same hart, sometimes another). The run this tour follows (traced with gdb) is one real interleaving among many; your boot will differ in the details, never in the outcome.

Best after: 3. main: one hart builds the kernel, the others wait

Who is running where

Hart What it is doing
0 Finishing main: about to call userinit
1 Spinning on started, waiting for hart 0 (Tour 3: main: one hart builds the kernel, the others wait)
2 The same as hart 1

Interrupts are off on all three harts. The process table is empty.

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. 1allocproc claims a slot under its lock kernel/proc.c
  2. 2A trapframe, a page table and a kernel context kernel/proc.c
  3. 3userinit marks process 1 runnable kernel/proc.c
  4. 4Three schedulers race for process 1 kernel/proc.c
  5. 5swtch saves the scheduler kernel/swtch.S
  6. 6ld sp: onto process 1's empty kernel stack kernel/swtch.S
  7. 7forkret, the first code process 1 runs kernel/proc.c
  8. 8fsinit reads block 1 kernel/fs.c
  9. 9Waiting for the disk, and waking up somewhere else kernel/virtio_disk.c
  10. 10initlog creates the last boot lock kernel/log.c
  11. 11kexec opens /init kernel/exec.c
  12. 12Loading the two segments kernel/exec.c
  13. 13The user stack and the point of no return kernel/exec.c
  14. 14forkret heads for user mode kernel/proc.c
  15. 15prepare_return records hart 0 in the trapframe kernel/trap.c
  16. 16userret switches to init's page table kernel/trampoline.S
  17. 17ld sp, then sret: user mode for the first time kernel/trampoline.S
  18. 18init looks for the console and does not find it user/init.c
  19. 19mknod creates inode 23 kernel/sysfile.c
  20. 20open returns descriptor 0 kernel/sysfile.c
  21. 21dup, dup: three descriptors, one file kernel/sysfile.c
  22. 22init: starting sh, then fork user/init.c
  23. 23kfork and the lock dance kernel/proc.c
  24. 24init waits kernel/proc.c
  25. 25The shell prints its prompt user/sh.c
  26. 26sh blocks in read, waiting for you kernel/console.c
  27. 27The machine at rest kernel/proc.c

Keys: ← → step · Home start