1Where is a write to a shared page caught?
Parent and child must be able to share a page until one of them writes it. Something has to notice that write before it lands in the shared page. What notices it, and what does the kernel learn when it does? Commit to an answer before the hints: is it a check in software, or something the hardware does on every store?
Look at the PTE bits in kernel/riscv.h:395 and at how usertrap handles scause 13 and 15 today (kernel/trap.c:71).
The PTE (page-table entry) of a user page is consulted by the hardware on every user load and store. A store through a PTE without PTE_W cannot complete: the hart takes a store page fault, puts the faulting virtual address in stval, and leaves sepc pointing at the store so it can be retried.
Map the shared page without PTE_W in both page tables. The fault handler gives the faulting process its own writable copy, replaces its PTE, and returns to the same instruction, which now succeeds. Nothing else in the process notices.
The reference design
The hardware catches it. On every user-mode store, the Sv39 walk checks PTE_W; if
it is clear, the store does not happen and the hart traps with scause = 15 (store/AMO
page fault), stval = the virtual address, and sepc = the address of the store
instruction. Because usertrap does not add 4 to epc for a fault (only for
ecall), returning to user space runs the store again.
So the design is: clear PTE_W on every shared page that used to be writable, in the
parent’s page table as well as the child’s; on a store fault to such a page, allocate a
page, copy the 4096 bytes, point the faulting process’s PTE at the copy with PTE_W set,
drop one reference to the old page, and return. The trap costs one trip through
uservec and usertrap, paid only for pages that are actually written. A
software check would have to be compiled into every store of every program, which is
impossible.
One thing the hardware does not catch: stores made by the kernel. They are the subject of a later question.
Check yourself
A child shares its parent’s stack page, mapped without PTE_W. The child executes
sd ra, 8(sp) (a store) to that page. What value does usertrap read from
scause?
True or false: after a copy-on-write fault is handled, usertrap must add 4 to
p->trapframe->epc so that the process does not execute the faulting store twice.
Why?