Add a few sbrktest for lazy allocatioin lab
This commit is contained in:
parent
936afc6e1a
commit
0387e2156f
132
labs/lazy.html
Normal file
132
labs/lazy.html
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lab: xv6 lazy page allocation</title>
|
||||||
|
<link rel="stylesheet" href="homework.css" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Lab: xv6 lazy page allocation</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
One of the many neat tricks an O/S can play with page table hardware
|
||||||
|
is lazy allocation of heap memory. Xv6 applications ask the kernel for
|
||||||
|
heap memory using the sbrk() system call. In the kernel we've given
|
||||||
|
you, sbrk() allocates physical memory and maps it into the process's
|
||||||
|
virtual address space. There are programs that allocate memory but
|
||||||
|
never use it, for example to implement large sparse arrays.
|
||||||
|
Sophisticated kernels delay allocation of each page of memory until
|
||||||
|
the application tries to use that page -- as signaled by a page fault.
|
||||||
|
You'll add this lazy allocation feature to xv6 in this lab.
|
||||||
|
|
||||||
|
<h2>Part One: Eliminate allocation from sbrk()</h2>
|
||||||
|
|
||||||
|
Your first task is to delete page allocation from the sbrk(n) system
|
||||||
|
call implementation, which is the function sys_sbrk() in sysproc.c. The
|
||||||
|
sbrk(n) system call grows the process's memory size by n bytes, and
|
||||||
|
then returns the start of the newly allocated region (i.e., the old
|
||||||
|
size). Your new sbrk(n) should just increment the process's size
|
||||||
|
(myproc()->sz) by n and return the old size. It should not allocate memory
|
||||||
|
-- so you should delete the call to growproc() (but you still need to
|
||||||
|
increase the process's size!).
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Try to guess what the result of this modification will be: what will
|
||||||
|
break?
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Make this modification, boot xv6, and type <tt>echo hi</tt> to the shell.
|
||||||
|
You should see something like this:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
init: starting sh
|
||||||
|
$ echo hi
|
||||||
|
usertrap(): unexpected scause 0x000000000000000f pid=3
|
||||||
|
sepc=0x00000000000011dc stval=0x0000000000004008
|
||||||
|
va=0x0000000000004000 pte=0x0000000000000000
|
||||||
|
panic: unmappages: not mapped
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The "usertrap(): ..." message is from the user trap handler in trap.c;
|
||||||
|
it has caught an exception that it does not know how to handle. Make
|
||||||
|
sure you understand why this page fault occurs. The "stval=0x0..04008"
|
||||||
|
indicates that the virtual address that caused the page fault is
|
||||||
|
0x4008.
|
||||||
|
|
||||||
|
<h2>Part Two: Lazy allocation</h2>
|
||||||
|
|
||||||
|
Modify the code in trap.c to respond to a page fault from user space
|
||||||
|
by mapping a newly-allocated page of physical memory at the faulting
|
||||||
|
address, and then returning back to user space to let the process
|
||||||
|
continue executing. You should add your code just before
|
||||||
|
the <tt>printf</tt> call that produced the "usertrap(): ..."
|
||||||
|
message.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hint: look at the printf arguments to see how to find the virtual
|
||||||
|
address that caused the page fault.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hint: steal code from allocuvm() in vm.c, which is what sbrk()
|
||||||
|
calls (via growproc()).
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hint: use PGROUNDDOWN(va) to round the faulting virtual address
|
||||||
|
down to a page boundary.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hint: <tt>usertrapret()</tt> in order to avoid
|
||||||
|
the <tt>printf</tt> and the <tt>myproc()->killed = 1</tt>.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hint: you'll need to call mappages().
|
||||||
|
|
||||||
|
<p>Hint: you can check whether a fault is a page fault by r_scause()
|
||||||
|
is 13 or 15 in trap().
|
||||||
|
|
||||||
|
<p>Hint: modify unmappages() to not free pages that aren't mapped.
|
||||||
|
|
||||||
|
<p>Hint: if the kernel crashes, look up sepc in kernel/kernel.asm
|
||||||
|
|
||||||
|
<p>Hint: if you see the error "imcomplete type proc", include "proc.h"
|
||||||
|
(and "spinlock.h").
|
||||||
|
|
||||||
|
<p>Hint: the first test in sbrk() allocates something large, this
|
||||||
|
should succeed now.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If all goes well, your lazy allocation code should result in <tt>echo
|
||||||
|
hi</tt> working. You should get at least one page fault (and thus lazy
|
||||||
|
allocation) in the shell, and perhaps two.
|
||||||
|
|
||||||
|
<p>If you have the basics working, now turn your implementation into
|
||||||
|
one that handles the corner cases too.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> Handle negative sbrk() arguments. sbrktest() in usertests will
|
||||||
|
tests this.
|
||||||
|
|
||||||
|
<li> Handle fork correctly. sbrktst() will test this.
|
||||||
|
|
||||||
|
<li> Make sure that kernel use of not-yet-allocated user addresses
|
||||||
|
works; for example, if a program passes an sbrk()-allocated
|
||||||
|
address to write(). sbrktest() will test this.
|
||||||
|
|
||||||
|
<li> Handle out of memory correctly. sbrktst() will test this.
|
||||||
|
|
||||||
|
<li> Handle faults on the invalid page below the stack. stacktest()
|
||||||
|
in usertests will tests this.
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>Run all tests in usertests() to make sure your solution doesn't
|
||||||
|
break other tests.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<div class="question">
|
||||||
|
<p><b>Submit</b>: The code that you added to trap.c in a file named <em>hwN.c</em> where <em>N</em> is the homework number as listed on the schedule.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1577,6 +1577,8 @@ sbrktest(void)
|
||||||
int i, fds[2], pids[10], pid, ppid;
|
int i, fds[2], pids[10], pid, ppid;
|
||||||
char *c, *oldbrk, scratch, *a, *b, *lastaddr, *p;
|
char *c, *oldbrk, scratch, *a, *b, *lastaddr, *p;
|
||||||
uint64 amt;
|
uint64 amt;
|
||||||
|
int fd;
|
||||||
|
int n;
|
||||||
#define BIG (100*1024*1024)
|
#define BIG (100*1024*1024)
|
||||||
|
|
||||||
printf(stdout, "sbrk test\n");
|
printf(stdout, "sbrk test\n");
|
||||||
|
@ -1707,6 +1709,50 @@ sbrktest(void)
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test running fork with the above allocated page
|
||||||
|
ppid = getpid();
|
||||||
|
pid = fork();
|
||||||
|
if(pid < 0){
|
||||||
|
printf(stdout, "fork failed\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// test out of memory during sbrk
|
||||||
|
if(pid == 0){
|
||||||
|
// allocate a lot of memory
|
||||||
|
a = sbrk(0);
|
||||||
|
sbrk(10*BIG);
|
||||||
|
int n = 0;
|
||||||
|
for (i = 0; i < 10*BIG; i += 4096) {
|
||||||
|
n += *(a+i);
|
||||||
|
}
|
||||||
|
printf(stdout, "allocate a lot of memory succeeded %d\n", n);
|
||||||
|
kill(ppid);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// test reads from allocated memory
|
||||||
|
a = sbrk(4096);
|
||||||
|
fd = open("sbrk", O_CREATE|O_WRONLY);
|
||||||
|
unlink("sbrk");
|
||||||
|
if(fd < 0) {
|
||||||
|
printf(stdout, "open sbrk failed\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
if ((n = write(fd, a, 10)) < 0) {
|
||||||
|
printf(stdout, "write sbrk failed\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
// test writes to allocated memory
|
||||||
|
a = sbrk(4096);
|
||||||
|
if(pipe((int *) a) != 0){
|
||||||
|
printf(1, "pipe() failed\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
if(sbrk(0) > oldbrk)
|
if(sbrk(0) > oldbrk)
|
||||||
sbrk(-(sbrk(0) - oldbrk));
|
sbrk(-(sbrk(0) - oldbrk));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue