xv6, line by line
lab 12
Lab 1212 Demand-paged exec

Lab 12 · reveal · 19 steps · 11 commits

Demand-paged exec: the reference solution

In this tree kexec reads a whole program into memory before the program runs its first instruction. For each loadable segment of the ELF file it allocates every page and fills it with readi (loadseg). usertests is 16 pages; echo is 2, and the second, its bss, is never touched when you type echo hi. In this lab exec only records where each segment lives in the file, keeps the file, and lets the program start with no text and no data at all. Each page is read from the file the first time something touches it.

The idea is old (it is how Unix has run programs since the late 1970s), and it touches more of the kernel than you might expect. Who notices the first touch of a page, and what does that have to do with the lazy heap this tree already has? Reading a file means locking an inode and waiting for the disk, from inside a fault handler: where can such a fault happen, and what does the kernel hold at that moment? What happens when a program reads its own file into a page of itself that is not there yet? What keeps the file alive while it runs, and what does a running program see when someone deletes, truncates or rewrites it? The think section asks these questions in the order a designer meets them.

The reference solution is eleven small commits. With it, dexectest lazy loads 8 of the 25 pages of its image, and each page it touches later costs exactly one page of memory.

Each step shows one change on the branch ext/12-demand-exec, the code around it, and the state of the machine when that code runs.

The route
  1. 1exec keeps the file it read kernel/exec.c
  2. 2Dropping the old program, inside the transaction kernel/exec.c
  3. 3The child inherits the file, exit lets it go kernel/proc.c
  4. 4exit drops the reference inside its transaction kernel/proc.c
  5. 5A record per segment kernel/proc.h
  6. 6exec records each segment kernel/exec.c
  7. 7loadpage: find the segment, zero the page kernel/exec.c
  8. 8loadpage: read the file's part, under the inode lock kernel/exec.c
  9. 9vmfault: below the image, the page comes from the file kernel/vm.c
  10. 10Read the trap registers before anything can sleep kernel/trap.c
  11. 11The instruction page fault joins the branch kernel/trap.c
  12. 12uvmprefault: load before you lock kernel/vm.c
  13. 13read and write call it first kernel/sysfile.c
  14. 14wait copies under two spinlocks kernel/sysproc.c
  15. 15usertests loads itself before it counts user/usertests.c
  16. 16The switch kernel/exec.c
  17. 17sbrk below the image kernel/exec.c
  18. 18The test the original kernel fails user/dexectest.c
  19. 19The last process lets go of a deleted program kernel/proc.c

Keys: ← → step · Home start