MIT6.s081 lab cow
LAB:cow
1.实验概述
本次实验的内容为给xv6实现一个cow功能,具体情况为,在fork
时,子进程一般会立即复制父进程的内存并获取一段新的内存,但这些内存并不是会立即使用的,所谓我们要实现的功能为,在fork时,不给子进程分配内存,而是直接将子进程的pagetable
和父进程映射同一段物理内存,随后当需要修改这段内存时,触发一个pagefault
,在中断处理程序中分配内存。
2.具体实现
首先修改uvmcopy
函数,为了在后续修改内存时触发pagetable
,我们需要将父进程和子进程的pagetable
上的对应ptes
标记位PTE_W
去除并添加PTE_C
。
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
pte_t *pte;
uint64 pa, i;
uint flags;
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 |= PTE_C;
flags = PTE_FLAGS(*pte);
if(mappages(new, i, PGSIZE, pa, flags) != 0)
goto err;
addpagecount((void *)pa, 1);
}
return 0;
err:
uvmunmap(new, 0, i / PGSIZE, 0);
return -1;
}
在usertrap
中分配新内存,并复制原物理地址中的内容到其中,修改pagetable
上的对应ptes
,同时尝试释放原物理地址(因为根据物理地址无法检索到映射它的两个虚拟地址,例如有一个父进程和一个子进程同时映射一块物理地址,两个进程访问到那块PTE_W
标记位为0的物理块时,都会复制出一块新内存并尝试释放这个物理块)。
// usertrap
else if(r_scause() == 15){
if (cowhandler() == -1) {
p->killed = 1;
}
}
int
cowhandler() {
struct proc *p = myproc();
uint64 va = r_stval();
if (va > MAXVA)
return -1;
pte_t *pte = walk(p->pagetable, va, 0);
if (*pte & PTE_C) {
uint64 pa = PTE2PA(*pte);
uint64 ka = (uint64)kalloc();
if (ka == 0) {
return -1;
} else {
memmove((char *)ka, (char *)pa, PGSIZE);
kfree((void *)pa);
uint flags = PTE_FLAGS(*pte);
*pte = PA2PTE(ka) | flags | PTE_W;
*pte &= ~PTE_C;
}
}
return 0;
}
现在需要处理kalloc.c
中的函数
int page_count[PHYSTOP/PGSIZE];
struct spinlock pc_lock;
void
kinit()
{
initlock(&kmem.lock, "kmem");
initlock(&pc_lock, "pc_lock");
freerange(end, (void*)PHYSTOP);
}
void
freerange(void *pa_start, void *pa_end)
{
char *p;
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) {
page_count[PA2INDEX(p)] = 1;
kfree(p);
}
}
void
kfree(void *pa)
{
struct run *r;
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
acquire(&pc_lock);
if (page_count[PA2INDEX(pa)] < 1)
panic("kfree");
page_count[PA2INDEX(pa)] -= 1;
int tmp = page_count[PA2INDEX(pa)];
release(&pc_lock);
if (tmp == 0) {
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
r = (struct run*)pa;
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
}
}
void *
kalloc(void)
{
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if(r) {
kmem.freelist = r->next;
acquire(&pc_lock);
page_count[PA2INDEX((void *)r)] = 1;
release(&pc_lock);
}
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
int
addpagecount(void *pa, int n)
{
acquire(&pc_lock);
page_count[PA2INDEX(pa)] += n;
release(&pc_lock);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通