From dfc2cf9123a558670dceca880e1d9f4d2f236a7a Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Wed, 24 Jul 2019 08:55:41 -0400 Subject: [PATCH] add rtm's cow lab --- labs/cow.html | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 labs/cow.html diff --git a/labs/cow.html b/labs/cow.html new file mode 100644 index 0000000..c8a0c1d --- /dev/null +++ b/labs/cow.html @@ -0,0 +1,99 @@ +

Programming Assignment: Copy-on-Write Fork for xv6

+ +

+Your task is implement copy-on-write fork in the xv6 kernel. You are +done if your modified kernel executes both the cow and usertests +programs successfully. + +

The problem

+ +The fork() system call in xv6 copies all of the parent process's +user-space memory into the child. If the parent is large, copying can +take a long time. In addition, the copies often waste memory; in many +cases neither the parent nor the child modifies a page, so that in +principle they could share the same physical memory. The inefficiency +is particularly clear if the child calls exec(), since then most of +the copied pages are thrown away without ever being used. Of course, +sometimes both child and parent modify memory at the same virtual +address after a fork(), so for some pages the copying is truly needed. + +

The solution

+ +The goal of copy-on-write (COW) fork() is to defer allocating and +copying physical memory pages for the child until they are actually +needed, in the hope that they may never be needed. + +

+COW fork() creates just a pagetable for the child, with PTEs for user +memory pointing to the parent's physical pages. COW fork() marks all +the user PTEs in both parent and child as read-only. When either +process tries to write one of these COW pages, the CPU will force a +page fault. The kernel page-fault handler detects this case, allocates +a page of physical memory for the faulting process, copies the +original page into the new page, and modifies the relevant PTE in the +faulting process to refer to the new page, this time with the PTE +marked writeable. When the page fault handler returns, the user +process will be able to write its copy of the page. + +

+COW fork() makes freeing of the physical pages that implement user +memory a little trickier. A given physical page may be referred to by +multiple processes' page tables, and should be freed when the last +reference disappears. + +

The cow test program

+ +To help you test your implementation, we've provided an xv6 program +called cow (source in user/cow.c). cow runs various tests, but +even the first will fail on unmodified xv6. Thus, initially, you +will see: + +
+$ cow
+simple: fork() failed
+$ 
+
+ +The "simple" test allocates more than half of available physical +memory, and then fork()s. The fork fails because there is not enough +free physical memory to give the child a complete copy of the parent. + +

+When you are done, your kernel should be able to run both cow and +usertests. That is: + +

+$ cow
+simple: ok
+simple: ok
+three: zombie!
+ok
+three: zombie!
+ok
+three: zombie!
+ok
+file: ok
+ALL COW TESTS PASSED
+$ usertests
+...
+ALL TESTS PASSED
+$
+
+ +

Hints

+ +Here's one reasonable plan of attack. Modify uvmcopy() to map the +parent's physical pages into the child, instead of allocating new +pages, and clear PTE_W in the PTEs of both child and parent. +Modify usertrap() to recognize a page fault. When a page fault occurs +on a COW page, allocate a new page with kalloc(), copy the old page to +the new page, and install the new page in the PTE with PTE_W set. +Next, ensure that each physical page is freed when the last PTE +reference to it goes away (but not before!), perhaps by implementing +reference counts in kalloc.c. Finally, modify copyout() to use the +same scheme as page faults when it encounters a COW page. + +

+It may be useful to have a way to record, for each PTE, whether it is +a COW mapping. You can use the RSW (reserved for software) bits in +the RISC-V PTE for this.