add rtm's cow lab
This commit is contained in:
		
							parent
							
								
									a41365faa3
								
							
						
					
					
						commit
						dfc2cf9123
					
				
					 1 changed files with 99 additions and 0 deletions
				
			
		
							
								
								
									
										99
									
								
								labs/cow.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								labs/cow.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
<h2>Programming Assignment: Copy-on-Write Fork for xv6</h2>
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<h3>The problem</h3>
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<h3>The solution</h3>
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<h3>The cow test program</h3>
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
$ cow
 | 
			
		||||
simple: fork() failed
 | 
			
		||||
$ 
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
When you are done, your kernel should be able to run both cow and
 | 
			
		||||
usertests. That is:
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
$ cow
 | 
			
		||||
simple: ok
 | 
			
		||||
simple: ok
 | 
			
		||||
three: zombie!
 | 
			
		||||
ok
 | 
			
		||||
three: zombie!
 | 
			
		||||
ok
 | 
			
		||||
three: zombie!
 | 
			
		||||
ok
 | 
			
		||||
file: ok
 | 
			
		||||
ALL COW TESTS PASSED
 | 
			
		||||
$ usertests
 | 
			
		||||
...
 | 
			
		||||
ALL TESTS PASSED
 | 
			
		||||
$
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
<h3>Hints</h3>
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
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.
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue