109 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <html>
 | |
| <head>
 | |
| <title>Lab: Copy-on-Write Fork for xv6</title>
 | |
| <link rel="stylesheet" href="homework.css" type="text/css" />
 | |
| </head>
 | |
| <body>
 | |
| 
 | |
| <h1>Lab: 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.
 | |
| 
 | |
| <h2>The problem</h2>
 | |
| 
 | |
| 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.
 | |
| 
 | |
| <h2>The solution</h2>
 | |
| 
 | |
| 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.
 | |
| 
 | |
| <h2>The cow test program</h2>
 | |
| 
 | |
| 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>
 | |
| 
 | |
| <h2>Hints</h2>
 | |
| 
 | |
| 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.
 | |
| 
 | |
| </body>
 | |
| </html>
 | 
