cow lab finished

This commit is contained in:
2025-07-03 10:28:51 +08:00
parent 66657d1ca0
commit c8bae855c5
3 changed files with 162 additions and 1 deletions

159
cowlab.md Normal file
View File

@ -0,0 +1,159 @@
# xv6-labs: Copy-On-Write (COW) Fork 实验详细总结
## 实验目标
实现 xv6 操作系统的 COW写时复制fork
- fork 时父子进程共享物理页,只有在写入时才真正分配和复制物理页。
- 提高 fork+exec 的效率,减少不必要的内存拷贝。
## 主要实现步骤与详细代码
### 1. 物理页引用计数
**文件kernel/kalloc.c**
- 在文件顶部添加:
```c
#define NPAGE ((PHYSTOP - KERNBASE) / PGSIZE)
static int refcnt[NPAGE];
static inline int pa2idx(void *pa) { return ((uint64)pa - KERNBASE) / PGSIZE; }
```
- kalloc 分配新页时:
```c
if(r) {
memset((char*)r, 5, PGSIZE);
int idx = pa2idx((void*)r);
refcnt[idx] = 1;
}
```
- kfree 释放页时:
```c
int idx = pa2idx(pa);
acquire(&kmem.lock);
if(refcnt[idx] > 1) {
refcnt[idx]--;
release(&kmem.lock);
return;
}
refcnt[idx] = 0;
release(&kmem.lock);
memset(pa, 1, PGSIZE);
// ...加入空闲链表...
```
- 增加辅助函数:
```c
void incref(void *pa) { refcnt[pa2idx(pa)]++; }
void decref(void *pa) { refcnt[pa2idx(pa)]--; }
int getref(void *pa) { return refcnt[pa2idx(pa)]; }
```
### 2. PTE 标记 COW
**文件kernel/riscv.h**
```c
#define PTE_COW (1L << 8) // 使用RSW位标记COW
```
### 3. 修改 uvmcopy 实现 COW fork
**文件kernel/vm.c**
- 在文件头部声明:
```c
void incref(void *pa);
```
- 修改 uvmcopy
```c
int uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) {
...
for(i = 0; i < sz; i += PGSIZE){
...
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
if(flags & PTE_W) {
flags = (flags & ~PTE_W) | PTE_COW;
*pte = PA2PTE(pa) | flags;
}
if(mappages(new, i, PGSIZE, pa, flags) != 0){
uvmunmap(new, 0, i / PGSIZE, 0);
return -1;
}
incref((void*)pa);
}
return 0;
}
```
### 4. usertrap 处理写页错误
**文件kernel/trap.c**
- 在文件头部声明:
```c
void kfree(void *pa);
void *kalloc(void);
```
- 在 usertrap() 添加COW处理
```c
else if((scause == 15 || scause == 13)) { // store/load page fault
pte_t *pte = walk(p->pagetable, stval, 0);
if(pte && (*pte & PTE_V) && (*pte & PTE_U) && (*pte & PTE_COW)) {
uint64 pa = PTE2PA(*pte);
char *mem = kalloc();
if(mem == 0) {
setkilled(p);
} else {
memmove(mem, (void*)pa, PGSIZE);
*pte = PA2PTE(mem) | (PTE_FLAGS(*pte) & ~PTE_COW) | PTE_W;
kfree((void*)pa);
}
} else {
setkilled(p);
}
}
```
### 5. copyout 处理 COW
**文件kernel/vm.c**
- 修改 copyout
```c
if((*pte & PTE_W) == 0) {
if((*pte & PTE_COW) != 0) {
pa0 = PTE2PA(*pte);
char *mem = kalloc();
if(mem == 0)
return -1;
memmove(mem, (void*)pa0, PGSIZE);
*pte = PA2PTE(mem) | PTE_FLAGS(*pte) | PTE_W;
*pte &= ~PTE_COW;
kfree((void*)pa0);
} else {
return -1;
}
}
```
### 6. 释放页时引用计数
- uvmunmap 释放页时直接调用 kfreekfree 内部已处理引用计数。
### 7. walk() 边界健壮性
**文件kernel/vm.c**
- 修改 walk()
```c
if(va >= MAXVA)
return 0;
```
## 调试与测试
- 通过 `make qemu`,运行 `usertests``cowtest`,确保所有测试通过。
- 重点关注 `MAXVAplus``forktest``sbrkfail` 等边界和异常测试。
- 遇到 panic("walk") 问题,修正 walk() 返回 0。
- 通过多次 fork/exec、写入、回收等场景验证内存共享和释放的正确性。
## 常见问题与修复
- **panic("walk")**walk() 对非法地址应返回0而不是panic。
- **refcnt数组不能用end做基址**应以KERNBASE为基址保证数组大小编译期可知。
- **重复定义PTE宏**只保留riscv.h中的定义。
## 总结
- COW fork 显著提升了 fork+exec 的效率。
- 通过引用计数和 COW 标志,保证了内存的正确共享与释放。
- 通过边界检查和优雅失败,保证了系统健壮性。
本实验加深了对操作系统内存管理、页表机制和异常处理的理解。

View File

@ -90,7 +90,8 @@ pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{
if(va >= MAXVA)
panic("walk");
return 0;
// panic("walk");
for(int level = 2; level > 0; level--) {
pte_t *pte = &pagetable[PX(level, va)];

1
time.txt Normal file
View File

@ -0,0 +1 @@
9