date: 2019-02-20
tags: OS 6.828
首先是要在sbrk
中去掉分配内存的部分。sbrk
函数原来的版本是:
int
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
if(growproc(n) < 0)
return -1;
return addr;
}
可想而知,分配内存的主要方式来源于growproc
(在proc.c
),growpoc
就是会给当前进程的page table加n的内存,并把proc->sz
加n
。注意,阅读allocuvm
函数可以得知,在分配的时候不会对n或者之后的sz+n
,round到page size的整数倍。所以按照题目要求,我们不分配内存,只增加sz,那么就是:
int
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
myproc()->sz += n;
return addr;
}
之后,如果启动xv6,就会得到需要的结果:
init: starting sh
$ echo hi
pid 3 sh: trap 14 err 6 on cpu 0 eip 0x112c addr 0x4004--kill proc
$
这里输出的是位于trap.c
中的page fault
,对应的是在trap.c
中跳入trap
函数。
按照题目要求,在trap中加入
case T_PGFLT:
{
// code from allocuvm
uint newsz = myproc()->sz;
uint a = PGROUNDDOWN(rcr2());
if(a < newsz){
char *mem = kalloc();
if(mem == 0) {
cprintf("out of memory\n");
exit();
break;
}
memset(mem, 0, PGSIZE);
mappages(myproc()->pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U);
}
break;
}
中间的代码都是由allocuvm
学来的,注意因为是lazy allocation所以只需要分配一个pagesize就可以了,同时a要PGROUNDDOWN
,而不是PGROUNDUP
。应该是因为在allocuvm
里面,传入的量是myproc()->sz
,是4096的倍数,而通过rcr2
得到的是第一个没有被分配的地址。可以共下面的这个我加了两行cprintf
的输出中看出来。
$ echo hi
addr: 16384
rcr2(): 16388
hi
所以,如果在trap
中仍然用PGROUNDUP
就会跳过去一个page了。但是为什么如果写错了触发的错误是mappages
中的panic(remap)
我就不明白了。
然后为了能访问mappages
,在trap.c
的最上面声明了
extern int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);
同时把其定义处的static
去掉。
然后为了实现Optional Challenges,把sbrk
改为:
int
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
if(n < 0) { // when n < 0, there is no lazy allocation
// 注意,growproc在释放没有被分配的内存的时候会直接跳过去,不会报错
if(growproc(n) < 0)
return -1;
} else {
if(myproc()->sz + n >= KERNBASE)
return -1;
myproc()->sz += n;
}
return addr;
}
从而实现了负数和大数的限制。没有写测试...所以不能确定对。
这里顺便记录一下xv6是如何调用trap
的。之前在讲system call的时候,说过是因为调用了int
指令。而page fault
这样的中断呢?
x86有一个特殊的table称为interrupt descriptot table(IDT),其为一个function handler的数组。这个数组里面调用的函数都在vector.S
中定义了。(注意vector.S
是由vector.pl
这个perl文件生成的。)每个function handler都调用了alltrap
从而调用trap
。在调用之前,会把trapframe推进栈中,对于page fault这样需要特殊寄存器的中断,会多推进去一些需要的东西。