xv6, line by line
lab 11
Lab 1111 Copy-on-write fork

Lab 11 · reveal · 17 steps · 6 commits

Copy-on-write fork: the reference solution

In this tree kfork copies every page of the parent with uvmcopy: one kalloc and one 4096-byte memmove per page. Most of that work is wasted. The shell forks a child that calls exec a few microseconds later and throws the copy away. In this lab you make fork share the parent’s pages instead: both page tables point at the same physical pages, mapped read-only, and the first store by either process takes a page fault that copies just that one page.

The idea fits in one sentence; the details are where an operating system shows its insides. A page can now belong to several page tables: when may it be freed, and what happens on three harts when its owners let go at the same moment? Does anything in this tree already handle store faults, and what would it make of yours? The kernel also writes into user memory on a process’s behalf: does that write fault too? And some pages are read-only for good, like program text: how will your fault handler know them from shared ones? The think section asks these questions in the order a designer meets them.

The reference solution is six small commits. With it, a fork of the shell allocates 6 pages instead of 11, and a process using 60% of RAM can fork, which the original kernel refuses.

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

The route
  1. 1One counter for every page of RAM kernel/kalloc.c
  2. 2Every page starts life counted once kernel/kalloc.c
  3. 3kfree drops one reference kernel/kalloc.c
  4. 4kalloc sets the count, and two small helpers kernel/kalloc.c
  5. 5A PTE bit that the hardware ignores kernel/riscv.h
  6. 6cowfault: is this really a copy-on-write page? kernel/vm.c
  7. 7The last sharer takes the page over kernel/vm.c
  8. 8Copy, remap, drop kernel/vm.c
  9. 9usertrap: a store fault now has two meanings kernel/trap.c
  10. 10When nobody can fix the fault kernel/trap.c
  11. 11copyout: the kernel's writes do not fault kernel/vm.c
  12. 12A copy under two spinlocks kernel/kalloc.c
  13. 13uvmcopy shares instead of copying kernel/vm.c
  14. 14The parent must stop writing too kernel/vm.c
  15. 15Undoing a fork that ran out of memory kernel/vm.c
  16. 16exec and exit change nothing kernel/vm.c
  17. 17The test that the original kernel fails user/cowtest.c

Keys: ← → step · Home start