在xv6系统中,fork()采取的措施是将父进程的内存全部复制给子进程,但子进程用不上其中的大部分资源,况且一般子进程后跟exec又会抛弃原本的内存,所以在fork的即时复制就极大的浪费资源。
copy-on-write(写时复制),大概策略如下,在fork()时,不给子进程即时分配物理页,而是只设置子进程的页表,且使得子进程和父进程的虚拟地址同时指向相同的物理地址并且设置页表中的pte都为只读,之后一旦父进程或者子进程由写内存请求时,一定会报页错误,此时再复制相应页的副本给子进程,然后修改父进程和子进程相应页的pte改为可写可读。大概类似于下图:

可以发现这种策略,极大加快了fork的效率,并且节省了内存的空间,仅在子进程和父进程有实质性的不同时才分别给两者分配各自的物理页,否则公用父进程的物理页。
Implement copy-on write
提示一
1.根据提示,加上PTE_C的标志位,且修改uvmcopy函数如下:
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
pte_t *pte;
uint64 pa, i;
uint flags;
// char *mem;
for(i = 0; i < sz; i += PGSIZE){
if((pte = walk(old, i, 0)) == 0)
panic("uvmcopy: pte should exist");
if((*pte & PTE_V) == 0)
panic("uvmcopy: page not present");
pa = PTE2PA(*pte);
*pte &= (~PTE_W); //清除pte的w标志
*pte |= PTE_C; //加上是C的页标
flags = PTE_FLAGS(*pte);
if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0)
{
return -1;
}
refcnt_inc(pa);
// if((mem = kalloc()) == 0)
// goto err;
// memmove(mem, (char*)pa, PGSIZE);
// if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
// kfree(mem);
// goto err;
// }
}
return 0;
// err:
// uvmunmap(new, 0, i / PGSIZE, 1);
// return -1;
}
提示二
2.和上一个实验一样,我们需要在trap.c中处理从用户态转到内核态的trap程序。上个实验中,页错误是13和15的Exception Code,但此题只有子进程和父进程尝试写内存时才会出错,观察13为读取页错误,所以这里只需要考虑15即可。

最低0.47元/天 解锁文章
6764

被折叠的 条评论
为什么被折叠?



