cow lab finished
This commit is contained in:
159
cowlab.md
Normal file
159
cowlab.md
Normal 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 释放页时直接调用 kfree,kfree 内部已处理引用计数。
|
||||
|
||||
### 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 标志,保证了内存的正确共享与释放。
|
||||
- 通过边界检查和优雅失败,保证了系统健壮性。
|
||||
|
||||
本实验加深了对操作系统内存管理、页表机制和异常处理的理解。
|
||||
@ -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)];
|
||||
|
||||
Reference in New Issue
Block a user