实验内容网址:https://xv6.dgs.zone/labs/requirements/lab5.html
本实验的代码分支:https://gitee.com/dragonlalala/xv6-labs-2020/tree/lazy3/
Eliminate allocation from sbrk()
关键点:p->sz的含义
思路:
sbrk(n)
系统调用将进程的内存大小增加n个字节,然后返回新分配区域的开始部分(即旧的大小)。新的sbrk(n)
应该只将进程的大小(myproc()->sz
)增加n,然后返回旧的大小。因此直接修改sys_sbrk()
函数,注释掉对growproc()函数的调用,直接修改p->sz。
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
// lab5-1
myproc()->sz += n; // increase size but not allocate memory
// if(growproc(n) < 0)
// return -1;
return addr;
}
进行 echo hi 测试,实验结果如图所示
Lazy allocation
这题才是本实验的主要部分,上面的实验在调用sbrk增加或者减少内存时并没有对物理内存进行操作,这会引起页面错误,本题就是为了解决内存增加时出现的页面错误。
思路&步骤
对于这部分的思路和步骤可以跟着提示进行展开。
- 在
kernel/trap.c
文件的usertrap()
函数中对r_scause()
为13或者15的情况进行处理,这两种情况是出现了页面错误。通过r_stval()
函数获得出现页面错误的虚拟地址。参考growproc()
函数中对kalloc()
和mappages()
的调用,为出现报错的虚拟地址申请1页物理地址(内存),是的,1页。物理内存申请成功后将虚拟地址映射到物理地址上。这段代码中有一点要注意的是如果出现物理内存申请不成功 或者映射失败 的情况,需要将p->killed
置为1,标记当前进程后就需要立即去exit(-1)
了,所以下面的代码我添加了goto
来实现这个效果。如果没加goto
,则会导致物理内存申请不成功后反而去初始化物理内存,导致其他错误。
...
else if((which_dev = devintr()) != 0){
// ok
} else if(r_scause() == 13 || r_scause() == 15){
// 出现报错的虚拟地址
uint64 va = r_stval();
// 虚拟地址高于sbrk分配的虚拟地址则杀死进程
// 申请1页物理内存
char* pa = kalloc();
if(pa == 0){
// 申请不成功
printf("alloc physical memory failed");
p->killed = 1;
goto end;
}
// 初始化物理内存
memset(pa, 0, PGSIZE);
// 映射, 将出错的虚拟地址向下舍入到页面边界,因为va所在的这一页还没有对应的物理内存
if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, PTE_W|PTE_X|PTE_R|PTE_U)){
// 映射失败,释放物理内存
kfree(pa);
p->killed = 1;
goto end;
}
}else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
p->killed = 1;
}
end:
if(p->killed){
exit(-