该实验会实现一个内存的延时分配功能
Part 1 Eliminate allocation from sbrk() (easy)
实验目标
- 修改
sbrk()
系统调用,并不实际分配物理内存页,但增加当前进程的sz
,使得可以发生页面错误,陷入usertrap
,为后续usertrap
中处理页面错误做准备
分析讨论
sbrk()
系统调用的参数n
的取值讨论
a.n
可能为正数,也可能为负数,同时也需要对n的取值进行判断,不同的内存大小的时候,n
的范围会不同,但总的来说p->sz + n
需要在用户空间虚拟地址允许的范围内;
b.n
是正数,需要修改p->sz
,但并不实际分配内存;因为是延时分配,这里也不对n
的合法性进行判断(不能因为用户进程可能访问非法地址,就将其打死吧,等到它有访问非法地址的实质性行为,再给他判死刑),将判断的逻辑放到usertrap
处理中;
c.n
是负数,需要判断缩小后的内存大小是否合法,这里需要处理是因为,如果p->sz + n < 0
已经是非法行为了,所以这里就需要处理
Coding
// 该系统调用成功会返回调用后进程的实际内存大小,如果返回值 < -1,说明系统调用不成功
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
// 获取当前进程
struct proc* p = myproc();
addr = p->sz;
/*---------------- 修改开始部分 ----------------*/
uint64 sz = p->sz;
// 注释掉原来的内容,这部分会实际分配内存页
// if(growproc(n) < 0)
// return -1;
// 判断 n 正数、负数
if(n > 0) {
// 正数,延时分配
// lazy allocation
p->sz += n;
} else if(sz + n > 0) {
// 缩小后的内存大小还是大于0的,这里还有可能 n=0 的情况,但都可以到 uvmdealloc 处理
// uvmdealloc 会判断原来的内存大小oldsz,和新的内存大小newsz之间是否会释放内存页
// 最后返回处理后的进程的内存大小
sz = uvmdealloc(p->pagetable, sz, sz + n);
p->sz = sz;
} else {
// 非法的内存大小,
return -1;
}
/*---------------- 修改结束部分 ----------------*/
return addr;
}