xv6, line by line
tour 38
Tours38 Output to the console from three harts

Tour 38 · Devices and putting it all together · about 27 minutes · 15 steps

Output to the console from three harts

There is one screen and three harts. When several programs print at once, whose bytes go out first, and how finely can they be mixed? The answer in xv6 is precise, and it is decided by locks: a sleep lock that writers take turns on, a spinlock for the kernel’s own messages, and two paths to the UART that ignore each other.

This tour watches a real run. You type cat README & ; wc README at a freshly booted shell. cat prints the 2441-byte README with writes of 512 bytes; wc prints its one-line result with printf, one byte per system call; the shell prints its prompt; and you start typing again while all this is on the screen, so the kernel echoes your keys from an interrupt handler. Here is what QEMU actually showed, around byte 512 of README:

The following people have made contribution48 336 2441 README
$ s: Russ Cox (context switching,

wc’s line and the shell’s prompt landed in the middle of a word of cat’s output. By the end of the tour you will know exactly where such breaks can and cannot fall, why kernel messages behave differently, and what changes when the kernel panics. The single write path itself is Tour 5: Life of a system call; here we look at what happens when it is crowded.

Best after: 5. Life of a system call, 17. Sleep-locks, 37. A keystroke's journey

Who is running where

The machine has three harts. The shell (pid 2) forked pid 3 to run the command list; pid 3 forked pid 4 for the background part, which forked pid 5 to run cat and exited; pid 3 then ran wc itself. When the tour starts:

Hart What it is doing
0 Running cat README (pid 5), writing the file to the console 512 bytes at a time
1 Running wc README (pid 3), about to print its result
2 Idle in its scheduler; it will take your keystroke interrupts

The shell (pid 2) is asleep in kwait, waiting for pid 3 (wc). When wc exits, the shell will print $ , a third writer.

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. 1wc prints its answer user/wc.c
  2. 2printf makes one system call per byte user/printf.c
  3. 3cat writes 512 bytes at once user/cat.c
  4. 4consolewrite cuts every write into 32-byte batches kernel/console.c
  5. 5uartwrite holds tx_lock for one batch kernel/uart.c
  6. 6wc waits for the lock, asleep kernel/sleeplock.c
  7. 7Two queues, two channels kernel/uart.c
  8. 8Release wakes every waiter, and the race is open kernel/sleeplock.c
  9. 9You type, and the echo ignores tx_lock kernel/uart.c
  10. 10printk takes pr.lock for the whole call kernel/printk.c
  11. 11consputc goes straight to the UART kernel/console.c
  12. 12The shell's prompt, and what wc's 19 writes really were user/sh.c
  13. 13panic: one hart stops the world's printing kernel/printk.c
  14. 14After the panic, printing freezes the printer kernel/uart.c
  15. 15Three granularities, three locks kernel/console.c

Keys: ← → step · Home start