xv6, line by line
tour 40
Tours40 Capstone: the shell running ls | wc

Tour 40 · Devices and putting it all together · about 45 minutes · 25 steps

Capstone: the shell running ls | wc

You type seven characters and press Enter:

$ ls | wc
24 96 608
$

Less than a second later the answer is on the screen. In that second, xv6 used almost everything it has: the UART and the PLIC, interrupts on all three harts, the console’s line editor, five processes, three forks and two execs, a pipe, the path-name lookup, the inode table, the buffer cache, the virtio disk, the scheduler on every hart, processes going to sleep more than a dozen times (and wakeup called hundreds of times), and nearly 800 system calls. Every other tour on this site is a close-up of one stretch of this road.

This tour drives the whole road once, at a steady speed, in 25 stops. It follows not one process but five, so watch the machine-state display: at every step it says which process we are following, on which hart, in which mode, holding which locks. The story jumps between processes and harts the way the machine does, and each jump is a place where the kernel had to make sharing safe.

All the numbers are real. We ran this command on this build of xv6 with a tracing kernel and recorded which process ran where, every disk read, and every pipe read. Where the run could have gone differently (which hart wins a race), the tour says so.

Best after: 5. Life of a system call, 20. fork, 21. exit, wait and zombies, 22. exec, 37. A keystroke's journey, 39. Pipes

Who is running where

The machine has three harts. xv6 has just booted, and this is the first command. When the tour starts:

Hart What it is doing
0 Running the shell sh (pid 2), which has just printed $
1 Idle in its scheduler, waiting in wfi
2 Idle in its scheduler, waiting in wfi

init (pid 1) is asleep in kwait, as it will be for the whole tour. The cast of processes, in order of appearance:

pid Program Role
2 sh the interactive shell; reads the line, forks, waits
3 sh (copy) parses the line, builds the pipe, forks both sides, waits for both
4 sh → ls the left side: writes the directory listing into the pipe
5 sh → wc the right side: counts what comes out of the pipe
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. 1sh (pid 2): the prompt, and a read that will wait user/sh.c
  2. 2sh (pid 2): nothing typed yet, so sleep on &cons.r kernel/console.c
  3. 3No process: you press Enter, and hart 2 commits the line kernel/console.c
  4. 4sh (pid 2): awake on hart 1, eight reads later user/ulib.c
  5. 5sh (pid 2): fork a child to do the work, then wait user/sh.c
  6. 6sh (pid 2): kfork copies memory and shares open files kernel/proc.c
  7. 7sh (pid 3): parsecmd builds a PIPE tree user/sh.c
  8. 8sh (pid 3): pipe() creates the channel user/sh.c
  9. 9sh (pid 3): two forks, and descriptors rewired user/sh.c
  10. 10sh (pid 3): close both ends, then wait kernel/proc.c
  11. 11ls (pid 4): exec loads the program from disk kernel/exec.c
  12. 12wc (pid 5): reads an empty pipe, and sleeps kernel/pipe.c
  13. 13ls (pid 4): open the current directory user/ls.c
  14. 14ls (pid 4): namex resolves "." to inode 1 kernel/fs.c
  15. 15ls (pid 4): reading a directory entry through the buffer cache kernel/fs.c
  16. 16ls (pid 4): a cache hit in bget kernel/bio.c
  17. 17ls (pid 4): stat every entry, and print it a byte at a time user/ls.c
  18. 18ls (pid 4): pipewrite stores a byte and wakes wc kernel/pipe.c
  19. 19wc (pid 5): wakes on hart 2 and drains what is there kernel/pipe.c
  20. 20wc (pid 5): counting, in user mode user/wc.c
  21. 21ls (pid 4): exit closes the write end kernel/proc.c
  22. 22wc (pid 5): end of file, and the answer user/wc.c
  23. 23wc (pid 5): eleven bytes to the screen kernel/uart.c
  24. 24sh (pid 3): two waits reap two children kernel/proc.c
  25. 25sh (pid 2): the next prompt user/sh.c

Keys: ← → step · Home start