本实验主要关于懒分配的知识。
前置:
一个广泛使用的特性叫做惰性分配——lazy allocation。它包括两部分内容:首先,当应用程序调用sbrk
时,内核增加地址空间,但在页表中将新地址标记为无效。其次,对于包含于其中的地址的页面错误,内核分配物理内存并将其映射到页表中。由于应用程序通常要求比他们需要的更多的内存,惰性分配可以称得上一次胜利: 内核仅在应用程序实际使用它时才分配内存。像COW fork一样,内核可以对应用程序透明地实现此功能。
Eliminate allocation from sbrk()
应该删除对growproc()
的调用,但是需要增加进程的大小。
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
// lazy allocation
myproc()->sz += n;
return addr;
}
Lazy allocation
修改trap.c中的代码以响应来自用户空间的页面错误,方法是新分配一个物理页面并映射到发生错误的地址,然后返回到用户空间,让进程继续执行。
usertrap
else if(r_scause()==13||r_scause()==15)
{//页面错误
uint64 va = r_stval();//获取异常地址
char *mem;
//对齐到页边界
va=PGROUNDDOWN(va);
if((mem = kalloc())==0)
{
//如果分配失败
panic("cannot allocate for lazy alloc\n");
exit(-1);
}
// 将物理页映射到进程的页表中,赋予写、执行、读和用户权限
if (mappages(p->pagetable, va, PGSIZE, (uint64)mem,
PTE_W | PTE_X | PTE_R | PTE_U) != 0)
{
// 如果映射失败,释放分配的内存,打印错误信息并退出当前进程
kfree(mem);
panic("cannot map for lazy alloc\n");
exit(-1);
}
}
else {
修改uvmunmap()
(kernel/vm.c),之所以修改这部分代码是因为lazy allocation中首先并未实际分配内存,所以当解除映射关系的时候对于这部分内存要略过,而不是使系统崩溃。
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if((pte = walk(pagetable, a, 0)) == 0)
//panic("uvmunmap: walk");
continue;
if((*pte & PTE_V) == 0)
// panic("uvmunmap: not mapped");
continue;
Lazytests and Usertests
处理sbrk()
参数为负数的情况,参考之前sbrk()
调用的growproc()
程序,如果为负数,就调用uvmdealloc()
函数,但需要限制缩减后的内存空间不能小于0
if (n < 0) {
// 如果需要减少内存大小 (n 为负值)
// 检查减少后的内存大小是否为负数
if ((myproc()->sz + n) < 0) {
// 如果减少后的内存大小小于 0,返回错误
return -1;
} else {
// 否则,尝试释放内存
// `addr` 是当前进程内存的末尾地址
// `addr + n` 是减少后的新的内存末尾地址
if (uvmdealloc(myproc()->pagetable, addr, addr + n) != (addr + n)) {
// 如果内存释放失败,返回错误
return -1;
}
}
}
fork()
中将父进程的内存复制给子进程的过程中用到了uvmcopy
,uvmcopy
原本在发现缺失相应的PTE等情况下会panic,这里也要continue
掉。在kernel/vm.c的uvmcopy
中.
if((pte = walk(old, i, 0)) == 0)
continue;
// panic("uvmcopy: pte should exist");
if((*pte & PTE_V) == 0)
continue;
// panic("uvmcopy: page not present");
当造成的page fault在进程的user stack以下(栈底)或者在p->sz
以上(堆顶)时,kill这个进程。在kernel/trap.c的usertrap
中增加以下判断条件
if ((va < p->sz) && (va > PGROUNDDOWN(p->trapframe->sp)))