Homework4: xv6 lazy page allocation
Xv6应用程序使用sbrk()系统调用向内核请求堆内存,sbrk()分配物理内存并将其映射到进程的虚拟地址空间。 有些程序分配内存,但从不使用它,例如大型稀疏数组。 复杂的内核会延迟分配内存的每个页面,直到应用程序尝试使用该页面为止(产生缺页中断时),本练习中我们会将此延迟分配功能添加到xv6。
Part One: Eliminate allocation from sbrk()
第一个任务是删除sbrk(n)系统调用中分配内存的核心代码,sbrk(n)会根据参数n增加分配给进程的内存,
然后返回新分配内存区域的起始地址,原来的sbrk(n)做两个工作:分配内存,增加myproc()->sz,我们写的
新sbrk(n)函数只根据参数n增加myproc()->sz,不做实际的分配工作。修改后的代码如下图:
修改后进行make qemu,进入xv6系统,当执行如下操作时,你会发现产生了某些错误:
"pid 3 sh: trap…" 这行信息是trap.c中的处理trap的函数发出的,trap号14代表发生了页故障,为什么
会发生页故障呢?
答:sbrk()实际上并没有分配内存,所以可以猜测是某个地方调用了该函数,然后对未分配的内存区域进行
了访问导致产生页故障,首先使用grep -n sbrk *.[chs]命令查看哪些地方调用了该函数:
发现了一个可疑点(上图红框部分),我们进入到umalloc.c中查看,发现malloc函数内调用了morecore函数,而morecore函数内调用了sbrk,再使用grep -n malloc *.[chs]进行查找:
发现sh.c中多处调用了malloc函数,查看sh.c文件,发现execcmd(),redircmd()等都调用了malloc函数,而
echo的命令类型是execcmd,因此我们修改execcmd函数代码和morecore函数代码(malloc函数中仅引用了一次morecore,且后续代码未访问分配的内存区域,不需修改)进行调试:
调试代码:
sh.c/execcmd:
morecore:
调试结果:
果然是在morecore函数中对未分配的内存进行访问导致的错误。
Part Two: Lazy allocation
第二部分要求我们修改trap.c中的代码通过新分配一个物理页并映射到故障地址的方式来解决这个页故障,之后返回到用户空间来使进程继续执行,应当在打印错误消息之前添加代码,代码无需涵盖所有极端情况和错误情况,能够满足让sh运行echo,ls之类的简单命令就可以了。
提示1:查看打印上面错误信息的那行cprintf,你就能知道找到产生故障的虚拟地址的方法。
提示2:可以参考vm.c中allocuvm函数的代码,该函数由sbrk()的子函数growproc()调用
提示3:记得使用PGROUNDDOWN(va)来保证发生故障的虚拟地址边界对齐
提示4:记得使用break或return 避免打印错误信息和设置myproc()->killed位
提示5:修改代码过程中需要调用mappages(),为此,需要删除vm.c中mappages()的static修饰符,并且需要在trap.c中对mappages()进行任何调用之前,声明此函数
提示6:可以通过检查tf->trapno是否等于T_PPGFLT来判断是否发生页故障。
修改后的trap代码:
测试通过:
这块代码分配给了进程在sbrk函数中未能取得的预期大小的空间,感觉跟前面延迟分配的描述有点不对应,应该是每次只分配一页,再修改一下。
最终代码: