警告
本文最后更新于 2023-04-15,文中内容可能已过时。
MIT 6.1810 中第五个 lab 的 solution
Your task is to implement copy-on-write fork in the xv6 kernel. You are done if your modified kernel executes both the cowtest and ‘usertests -q’ programs successfully.
将kernel/vm.c的uvmcopy()
修改成下边这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| extern void refinc(void *pa);
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_RSW;
flags = PTE_FLAGS(*pte);
if(mappages(new, i, PGSIZE, pa, flags) != 0){
goto err;
}
refinc((void *)pa);
}
return 0;
err:
uvmunmap(new, 0, i / PGSIZE, 1);
return -1;
}
|
这里的refinc是之后加在别的源文件里的函数
把同文件的copyout()
修改成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
uint64 n, va0, pa0, flags;
pte_t *pte;
while(len > 0){
va0 = PGROUNDDOWN(dstva);
if(va0 >= MAXVA)
return -1;
pte = walk(pagetable, va0, 0);
if(pte == 0 || ((*pte) & PTE_V) == 0 || ((*pte) & PTE_U) == 0) {
return -1;
}
flags = PTE_FLAGS(*pte);
pa0 = PTE2PA(*pte);
if ((flags & PTE_RSW) == PTE_RSW){
char* mem = kalloc();
memmove(mem, (char*)pa0, PGSIZE);
flags |= PTE_W;
flags &= ~PTE_RSW;
*pte &= ~PTE_V; // avoid remap
if (mappages(pagetable, va0, PGSIZE, (uint64)mem, flags) != 0) {
kfree((void *)mem);
return -1;
}
kfree((void*) pa0);
}
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
n = PGSIZE - (dstva - va0);
if(n > len)
n = len;
memmove((void *)(pa0 + (dstva - va0)), src, n);
len -= n;
src += n;
dstva = va0 + PGSIZE;
}
return 0;
}
|
kernel/riscv.h中对PTE权限设置那里加一个对RSW的定义
1
| #define PTE_RSW (1L << 8)
|
将kernel/trap.c添加一个page fault的识别逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| } else if(r_scause() == 15 || r_scause() == 13){
// store page fault or load page fault
pte_t *pte;
uint64 pa;
uint flags;
char *mem;
uint64 va = PGROUNDDOWN(r_stval());
if(va >= MAXVA || va < PGSIZE){
setkilled(p);
goto kill;
}
pte = walk(p->pagetable, va, 0);
if (pte == 0 || ((*pte) & PTE_V) == 0 || ((*pte) & PTE_U) == 0) {
setkilled(p);
goto kill;
}
pa = PTE2PA(*pte);
if(!(*pte & PTE_RSW))
goto unknown_trap;
*pte |= PTE_W;
*pte &= ~PTE_RSW;
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0){
setkilled(p);
goto kill;
}
memmove(mem, (char*)pa, PGSIZE);
uvmunmap(p->pagetable, va, 1, 0);
if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);
setkilled(p);
goto kill;
}
kfree((void*)pa);
}else {
unknown_trap:
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
setkilled(p);
}
kill:
if(killed(p))
exit(-1);
|
修改kernel/kalloc.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| struct {
struct spinlock lock;
int refcount[(PHYSTOP - KERNBASE) / PGSIZE];
} pginfo;
void
refinc(void *pa) {
int index = ((uint64)pa - KERNBASE) / PGSIZE;
acquire(&pginfo.lock);
pginfo.refcount[index]++;
release(&pginfo.lock);
}
void
kfree(void *pa)
{
struct run *r;
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
int index = ((uint64)pa - KERNBASE) / PGSIZE;
acquire(&pginfo.lock);
if (pginfo.refcount[index] > 1) {
pginfo.refcount[index]--;
release(&pginfo.lock);
}
else {
pginfo.refcount[index] = 0;
release(&pginfo.lock);
// 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;
release(&kmem.lock);
if (r) {
int index = ((uint64)r - KERNBASE) / PGSIZE;
acquire(&pginfo.lock);
pginfo.refcount[index] = 1;
release(&pginfo.lock);
}
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
|