10 Commits
pgtbl ... cow

Author SHA1 Message Date
c8bae855c5 cow lab finished 2025-07-03 10:28:51 +08:00
66657d1ca0 cowtest fixed and try to finish usertests 2025-07-03 10:13:07 +08:00
bc8921a77f cowlab bug fixed 2025-07-01 10:52:21 +08:00
c6fa25504f cow lab initialized 2025-06-28 09:57:36 +08:00
7c42fbed17 trapslab finished 2025-06-23 19:08:25 +08:00
70ee668b9b comment added 2025-06-23 11:18:49 +08:00
c7c962ffef alarm task finished 2025-06-18 16:02:24 +08:00
4c08583743 backtrace task finished 2025-06-14 17:09:29 +08:00
b63d0c9147 answers-traps.txt added 2025-06-12 11:43:43 +08:00
028cf61d61 trapslab initialized 2025-06-12 10:45:04 +08:00
42 changed files with 663 additions and 3188 deletions

2
.gitignore vendored
View File

@ -24,3 +24,5 @@ ph
barrier
/lab-*.json
.DS_Store
compile_commands.json

View File

@ -1,3 +1,4 @@
# To compile and run with a lab solution, set the lab name in conf/lab.mk
# (e.g., LAB=util). Run make grade to test solution with the lab's
# grade script (e.g., grade-lab-util).
@ -84,7 +85,7 @@ LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -Wall -Werror -O1 -fno-omit-frame-pointer -ggdb -gdwarf-2
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2
ifdef LAB
LABUPPER = $(shell echo $(LAB) | tr a-z A-Z)
@ -237,16 +238,15 @@ $U/_uthread: $U/uthread.o $U/uthread_switch.o $(ULIB)
$(OBJDUMP) -S $U/_uthread > $U/uthread.asm
ph: notxv6/ph.c
gcc -o ph -g -Ofast $(XCFLAGS) notxv6/ph.c -pthread
gcc -o ph -g -O2 $(XCFLAGS) notxv6/ph.c -pthread
barrier: notxv6/barrier.c
gcc -o barrier -g -Ofast $(XCFLAGS) notxv6/barrier.c -pthread
gcc -o barrier -g -O2 $(XCFLAGS) notxv6/barrier.c -pthread
endif
ifeq ($(LAB),pgtbl)
UPROGS += \
$U/_pgtbltest\
$U/_dirtypagestest
$U/_pgtbltest
endif
ifeq ($(LAB),lock)

View File

@ -0,0 +1,46 @@
xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
Version 6 (v6). xv6 loosely follows the structure and style of v6,
but is implemented for a modern RISC-V multiprocessor using ANSI C.
ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides
pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching,
locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements.
We are also grateful for the bug reports and patches contributed by
Takahiro Aoyagi, Marcelo Arroyo, Silas Boyd-Wickizer, Anton Burtsev,
carlclone, Ian Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed,
Asami Doi,Wenyang Duan, eyalz800, Nelson Elhage, Saar Ettinger, Alice
Ferrazzi, Nathaniel Filardo, flespark, Peter Froehlich, Yakir Goaron,
Shivam Handa, Matt Harvey, Bryan Henry, jaichenhengjie, Jim Huang,
Matúš Jókay, John Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95,
Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim
Kolontsov, Austin Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu,
Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark
Morrissey, mtasm, Joel Nider, Hayato Ohhashi, OptimisticSide,
phosphagos, Harry Porter, Greg Price, RayAndrew, Jude Rich, segfault,
Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya Shigemitsu, snoire,
Taojie, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Alissa Tung,
Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez,
Keiichi Watanabe, Lucas Wolf, Nicolas Wolovick, wxdao, Grant Wu, x653,
Jindong Zhang, Icenowy Zheng, ZhUyU1997, and Zou Chang Wei.
ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.1810, so we are more interested in
simplifications and clarifications than new features.
BUILDING AND RUNNING XV6
You will need a RISC-V "newlib" tool chain from
https://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for
riscv64-softmmu. Once they are installed, and in your shell
search path, you can run "make qemu".

View File

@ -1,153 +0,0 @@
### 背景信息
- **页面表结构**
- 页面大小(`PGSIZE`):通常为 4KB4096 字节)。
- 最大虚拟地址(`MAXVA`):在 Sv39 模式下为 2^39512GB
- 页面表条目PTE包含物理地址PA、权限位读/写/执行等以及有效位Valid bit
- 三级页面表PML4顶级L2中间级L1最低级每个页面表包含 512 条目2^9
- 虚拟地址VA39 位,分为 VPN[2]9 位顶级索引、VPN[1]9 位中间级索引、VPN[0]9 位,最低级索引)和 12 位页面偏移。
- **`print_pgtbl` 函数**
- 打印前 10 个页面VA 从 `0` 到 `9 * PGSIZE`)和最后 10 个页面(接近 `MAXVA`)的页面表条目。
- 调用 `print_pte`,输出虚拟地址对应的页面表条目信息。
### 输出分析
以下是提供的输出:
```
xv6 kernel is booting
hart 1 starting
hart 2 starting
page table 0x0000000087f4d000
0: pte 0x0000000021fd2401 pa 0x0000000087f49000
..0: pte 0x0000000021fd2001 pa 0x0000000087f48000
.. ..0: pte 0x0000000021fd281b pa 0x0000000087f4a000 va 0x0000000000000000 flags RXU
.. ..1: pte 0x0000000021fd1c17 pa 0x0000000087f47000 va 0x0000000000000001 flags RWU
.. ..2: pte 0x0000000021fd1807 pa 0x0000000087f46000 va 0x0000000000000002 flags RW
.. ..3: pte 0x0000000021fd1417 pa 0x0000000087f45000 va 0x0000000000000003 flags RWU
255: pte 0x0000000021fd3001 pa 0x0000000087f4c000
..511: pte 0x0000000021fd2c01 pa 0x0000000087f4b000
.. ..509: pte 0x0000000021fd5413 pa 0x0000000087f55000 va 0x0000000003fffffd flags RU
.. ..510: pte 0x0000000021fd5807 pa 0x0000000087f56000 va 0x0000000003fffffe flags RW
.. ..511: pte 0x000000002000180b pa 0x0000000080006000 va 0x0000000003ffffff flags RX
init: starting sh
```
#### 1. **输出结构与 `print_pgtbl` 的关联**
- 输出由 `print_pgtbl` 函数生成,包含:
- **顶级页面表地址**`page table 0x0000000087f4d000`(根页面表所在的物理地址)。
- **前几个页面表条目**(对应 `for (uint64 i = 0; i < 10; i++)` 循环):
- 打印顶级页面表索引 0 的条目,以及其下级页面表的条目。
- 具体为虚拟地址 `0x0` 到 `0x3000`(前 4 个页面)。
- **最后几个页面表条目**(对应 `for (uint64 i = top - 10; i < top; i++)` 循环):
- 打印顶级页面表索引 255 的条目以及其下级页面表的最后几个条目509511
- 对应虚拟地址接近 `MAXVA``0x3fffffd` 到 `0x3ffffff`)。
- 输出格式:
- 每行表示一个页面表条目,格式为:
```
[索引]: pte [PTE值] pa [物理地址] [va 虚拟地址 flags 权限]
```
- 缩进(`..`表示页面表层级顶级无缩进、L2`..`、L1`.. ..`)。
- 仅 L1 页面表条目包含虚拟地址(`va`)和权限标志(`flags`)。
#### 2. **页面表条目解析**
页面表条目PTE格式RISC-V Sv39
- **PTE 结构**64 位,包含:
- 物理页面号PPN44 位(高 44 位,右移 10 位得到物理页面地址)。
- 权限位VValid有效、RRead、WWrite、XExecute执行、UUser用户态可访问
- 其他标志GGlobal、AAccessed、DDirty等。
- **物理地址PA**:从 PTE 的 PPN 字段计算,`PA = PPN << 12`。
- **权限标志flags**:输出中的 `RXU`、`RWU`、`RW` 等表示权限组合:
- `R`:可读,`W`:可写,`X`:可执行,`U`:用户态可访问。
##### **前 4 个页面VA 0x0 到 0x3000**
- **顶级页面表(索引 0**
- `0: pte 0x0000000021fd2401 pa 0x0000000087f49000`
- PTE 值:`0x21fd2401`。
- 物理地址:`0x87f49000`L2 页面表地址)。
- 标志:`0x401` 的低 10 位为 `0x001`,表示 ValidV=1
- **L2 页面表(索引 0**
- `..0: pte 0x0000000021fd2001 pa 0x0000000087f48000`
- PTE 值:`0x21fd2001`。
- 物理地址:`0x87f48000`L1 页面表地址)。
- 标志ValidV=1
- **L1 页面表(索引 03**
- `.. ..0: pte 0x0000000021fd281b pa 0x0000000087f4a000 va 0x0000000000000000 flags RXU`
- 虚拟地址:`0x0`。
- 物理地址:`0x87f4a000`。
- 标志:`RXU`Read, Execute, User`0x1b` = `0b11011`V=1, R=1, X=1, U=1
- 用途:可能是内核代码段(可读、可执行、用户态可访问)。
- `.. ..1: pte 0x0000000021fd1c17 pa 0x0000000087f47000 va 0x0000000000000001 flags RWU`
- 虚拟地址:`0x1000`1 * PGSIZE
- 物理地址:`0x87f47000`。
- 标志:`RWU`Read, Write, User`0x17` = `0b10111`V=1, R=1, W=1, U=1
- 用途:可能是数据段或堆栈(可读、可写、用户态可访问)。
- `.. ..2: pte 0x0000000021fd1807 pa 0x0000000087f46000 va 0x0000000000000002 flags RW`
- 虚拟地址:`0x2000`2 * PGSIZE
- 物理地址:`0x87f46000`。
- 标志:`RW`Read, Write`0x07` = `0b00111`V=1, R=1, W=1
- 用途:可能是内核数据(可读、可写,仅内核态)。
- `.. ..3: pte 0x0000000021fd1417 pa 0x0000000087f45000 va 0x0000000000000003 flags RWU`
- 虚拟地址:`0x3000`3 * PGSIZE
- 物理地址:`0x87f45000`。
- 标志:`RWU`Read, Write, User`0x17` = `0b10111`V=1, R=1, W=1, U=1
- 用途:可能是用户态数据或堆栈。
##### **最后 3 个页面VA 0x3fffffd 到 0x3ffffff**
- **顶级页面表(索引 255**
- `255: pte 0x0000000021fd3001 pa 0x0000000087f4c000`
- PTE 值:`0x21fd3001`。
- 物理地址:`0x87f4c000`L2 页面表地址)。
- 标志ValidV=1
- **L2 页面表(索引 511**
- `..511: pte 0x0000000021fd2c01 pa 0x0000000087f4b000`
- PTE 值:`0x21fd2c01`。
- 物理地址:`0x87f4b000`L1 页面表地址)。
- 标志ValidV=1
- **L1 页面表(索引 509511**
- `.. ..509: pte 0x0000000021fd5413 pa 0x0000000087f55000 va 0x0000000003fffffd flags RU`
- 虚拟地址:`0x3fffffd000`(接近 MAXVA
- 物理地址:`0x87f55000`。
- 标志:`RU`Read, User`0x13` = `0b10011`V=1, R=1, U=1
- 用途:可能是用户态只读数据。
- `.. ..510: pte 0x0000000021fd5807 pa 0x0000000087f56000 va 0x0000000003fffffe flags RW`
- 虚拟地址:`0x3fffffe000`。
- 物理地址:`0x87f56000`。
- 标志:`RW`Read, Write`0x07` = `0b00111`V=1, R=1, W=1
- 用途:可能是内核数据(仅内核态)。
- `.. ..511: pte 0x000000002000180b pa 0x0000000080006000 va 0x0000000003ffffff flags RX`
- 虚拟地址:`0x3ffffff000`MAXVA - PGSIZE
- 物理地址:`0x80006000`。
- 标志:`RX`Read, Execute`0x0b` = `0b01011`V=1, R=1, X=1
- 用途:可能是内核代码或设备映射(可读、可执行,仅内核态)。
#### 3. **与 `print_pgtbl` 的关联**
- **前 10 个页面**
- 输出只显示了虚拟地址 `0x0` 到 `0x3000`4 个页面),而不是 10 个,可能是因为 `print_pgtbl` 的循环被修改或页面表中只有前 4 个页面有有效映射。
- 虚拟地址计算:
- `i * PGSIZE``i` 从 0 到 3与输出中的 `va 0x0`、`0x1000`、`0x2000`、`0x3000` 匹配。
- **最后 10 个页面**
- 输出显示索引 509511虚拟地址 `0x3fffffd000` 到 `0x3ffffff000`),对应 `i` 从 `top - 3` 到 `top - 1`。
- 计算 `top`
- `MAXVA = 2^39 = 512GB``PGSIZE = 4096``top = 512GB / 4096 = 2^27 = 134,217,728`。
- 虚拟地址 `0x3ffffff`(十进制 2^30 - 1对应页面号 `2^30 / 4096 = 2^18 - 1 = 262,143`。
- 顶级索引:`262,143 >> 18 = 255`(匹配 `255:`)。
- L2 索引:`(262,143 >> 9) & 0x1FF = 511`(匹配 `..511:`)。
- L1 索引:`262,143 & 0x1FF = 511`(匹配 `.. ..511:`)。
- 输出只显示最后 3 个页面509511可能是因为只有这些页面有有效映射。
- **缺失的 `print_pgtbl` 输出**
- 函数应打印 `"print_pgtbl starting\n"` 和 `"print_pgtbl: OK\n"`,但输出中缺失,可能是被其他日志覆盖或函数被修改。
#### 4. **页面表条目含义**
- **前 4 个页面**
- 映射到物理地址 `0x87f4a000` 到 `0x87f45000`,连续的物理页面。
- 权限多样(`RXU`、`RWU`、`RW`),表明这些页面用于不同用途:
- `RXU`:用户态代码(例如,初始用户程序)。
- `RWU`:用户态数据或堆栈。
- `RW`:内核数据(仅内核态)。
- **最后 3 个页面**
- 映射到物理地址 `0x87f55000` 到 `0x80006000`,不完全连续。
- 权限包括 `RU`(用户态只读)、`RW`(内核读写)、`RX`(内核代码)。
- 最后一个页面(`va 0x3ffffff000`, `pa 0x80006000`)可能是设备内存映射(常见于内核高地址空间)。
- **物理地址**
- 大部分物理地址在 `0x87fxxxxx` 范围内,表明内存分配集中在某一区域。
- 最后一个页面映射到 `0x80006000`,可能是设备寄存器或内核代码的固定映射。

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
LAB=pgtbl
LAB=cow

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 标志,保证了内存的正确共享与释放。
- 通过边界检查和优雅失败,保证了系统健壮性。
本实验加深了对操作系统内存管理、页表机制和异常处理的理解。

59
grade-lab-cow Normal file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(0, "running cowtest")
def test_cowtest():
r.run_qemu(shell_script([
'cowtest'
]), timeout=300)
@test(30, "simple", parent=test_cowtest)
def test_simple():
matches = re.findall("^simple: ok$", r.qemu.output, re.M)
assert_equal(len(matches), 2, "Number of appearances of 'simple: ok'")
@test(30, "three", parent=test_cowtest)
def test_three():
matches = re.findall("^three: ok$", r.qemu.output, re.M)
assert_equal(len(matches), 3, "Number of appearances of 'three: ok'")
@test(20, "file", parent=test_cowtest)
def test_file():
r.match('^file: ok$')
@test(20, "forkfork", parent=test_cowtest)
def test_forkfork():
r.match('^forkfork: ok$')
@test(0, "usertests")
def test_usertests():
r.run_qemu(shell_script([
'usertests -q'
]), timeout=1000)
r.match('^ALL TESTS PASSED$')
def usertest_check(testcase, nextcase, output):
if not re.search(r'\ntest {}: [\s\S]*OK\ntest {}'.format(testcase, nextcase), output):
raise AssertionError('Failed ' + testcase)
@test(5, "usertests: copyin", parent=test_usertests)
def test_sbrkbugs():
usertest_check("copyin", "copyout", r.qemu.output)
@test(5, "usertests: copyout", parent=test_usertests)
def test_sbrkbugs():
usertest_check("copyout", "copyinstr1", r.qemu.output)
@test(19, "usertests: all tests", parent=test_usertests)
def test_usertests_all():
r.match('^ALL TESTS PASSED$')
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(0, "pgtbltest")
def test_pgtbltest():
r.run_qemu(shell_script([
'pgtbltest'
]), timeout=300)
@test(10, "pgtbltest: ugetpid", parent=test_pgtbltest)
def test_ugetpid_():
r.match('^ugetpid_test: OK$')
@test(10, "pgtbltest: print_kpgtbl", parent=test_pgtbltest)
def test_print_kpgtbl_():
r.match(
'^page table 0x',
'^ \.\.0x0000000000000000',
'^ \.\. \.\.0x0000000000000000',
'^ \.\. \.\. \.\.0x0000000000000000',
'^ \.\. \.\. \.\.0x0000000000001000',
'^ \.\. \.\. \.\.0x0000000000002000',
'^ \.\. \.\. \.\.0x0000000000003000',
'^ \.\.(0xffffffffc0000000|0x0000003fc0000000)',
'^ \.\. \.\.(0xffffffffffe00000|0x0000003fffe00000)',
'^ \.\. \.\. \.\.(0xffffffffffffd000|0x0000003fffffd000)',
'^ \.\. \.\. \.\.(0xffffffffffffe000|0x0000003fffffe000)',
'^ \.\. \.\. \.\.(0xfffffffffffff000|0x0000003ffffff000)',
)
@test(10, "pgtbltest: pgaccess", parent=test_pgtbltest)
def test_nettest_():
r.match('^pgaccess_test: OK$')
@test(15, "pgtbltest: superpg", parent=test_pgtbltest)
def test_superpg_():
r.match('^superpg_test: OK$')
@test(5, "answers-pgtbl.txt")
def test_answers():
# just a simple sanity check, will be graded manually
check_answers("answers-pgtbl.txt")
@test(0, "usertests")
def test_usertests():
r.run_qemu(shell_script([
'usertests -q'
]), timeout=300)
@test(10, "usertests: all tests", parent=test_usertests)
def test_usertests():
r.match('^ALL TESTS PASSED$')
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -1,7 +1,3 @@
#ifdef LAB_MMAP
typedef unsigned long size_t;
typedef long int off_t;
#endif
struct buf;
struct context;
struct file;
@ -121,10 +117,6 @@ void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void push_off(void);
void pop_off(void);
int atomic_read4(int *addr);
#ifdef LAB_LOCK
void freelock(struct spinlock*);
#endif
// sleeplock.c
void acquiresleep(struct sleeplock*);
@ -181,17 +173,6 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
#if defined(LAB_PGTBL) || defined(SOL_MMAP)
void vmprint(pagetable_t);
#endif
#ifdef LAB_PGTBL
pte_t* pgpte(pagetable_t, uint64);
void superfree(void *pa);
void* superalloc();
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
uint64 sys_dirtypages(void);
#endif
// plic.c
void plicinit(void);
@ -206,31 +187,3 @@ void virtio_disk_intr(void);
// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
#ifdef LAB_LOCK
// stats.c
void statsinit(void);
void statsinc(void);
// sprintf.c
int snprintf(char*, unsigned long, const char*, ...);
#endif
#ifdef KCSAN
void kcsaninit();
#endif
#ifdef LAB_NET
// pci.c
void pci_init();
// e1000.c
void e1000_init(uint32 *);
void e1000_intr(void);
int e1000_transmit(char *, int);
// net.c
void netinit(void);
void net_rx(char *buf, int len);
#endif

View File

@ -128,10 +128,6 @@ exec(char *path, char **argv)
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
if (p->pid == 1) {
vmprint(p->pagetable);
}
return argc; // this ends up in a0, the first argument to main(argc, argv)
bad:

View File

@ -9,6 +9,12 @@
#include "riscv.h"
#include "defs.h"
#define NPAGE ((PHYSTOP - KERNBASE) / PGSIZE)
static int refcnt[NPAGE];
static inline int pa2idx(void *pa) {
return ((uint64)pa - KERNBASE) / PGSIZE;
}
void freerange(void *pa_start, void *pa_end);
extern char end[]; // first address after kernel.
@ -23,46 +29,25 @@ struct {
struct run *freelist;
} kmem;
struct super_run {
struct super_run *next;
};
struct {
struct spinlock lock;
struct super_run *freelist;
} skmem;
void superfree(void *pa) {
struct super_run *r;
if(((uint64)pa % SUPERPGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("superfree");
// Fill with junk to catch dangling refs.
memset(pa, 1, SUPERPGSIZE);
r = (struct super_run *)pa;
acquire(&skmem.lock);
r->next = skmem.freelist;
skmem.freelist = r;
release(&skmem.lock);
void incref(void *pa) {
int idx = pa2idx(pa);
refcnt[idx]++;
}
void* superalloc() {
struct super_run *r;
acquire(&skmem.lock);
r = skmem.freelist;
if(r) skmem.freelist = r->next;
release(&skmem.lock);
if(r) memset((void*)r, 0, SUPERPGSIZE);
return (void*)r;
void decref(void *pa) {
int idx = pa2idx(pa);
refcnt[idx]--;
}
int getref(void *pa) {
int idx = pa2idx(pa);
return refcnt[idx];
}
void
kinit()
{
initlock(&kmem.lock, "kmem");
initlock(&skmem.lock, "skmem");
for(int i = 0; i < NPAGE; i++)
refcnt[i] = 0;
freerange(end, (void*)PHYSTOP);
}
@ -71,22 +56,9 @@ freerange(void *pa_start, void *pa_end)
{
char *p;
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end - 12 * 1024 * 1024; p += PGSIZE) //留5个巨页
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
kfree(p);
p = (char*)SUPERPGROUNDUP((uint64)p);
for (; p + SUPERPGSIZE <= (char *)pa_end; p += SUPERPGSIZE) {
superfree(p);
}
}
// void
// freerange(void *pa_start, void *pa_end)
// {
// char *p;
// p = (char*)PGROUNDUP((uint64)pa_start);
// for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
// kfree(p);
// }
// Free the page of physical memory pointed at by pa,
// which normally should have been returned by a
@ -100,6 +72,16 @@ kfree(void *pa)
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
int idx = pa2idx(pa);
acquire(&kmem.lock);
if(refcnt[idx] > 1) {
refcnt[idx]--;
release(&kmem.lock);
return;
}
refcnt[idx] = 0;
release(&kmem.lock);
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
@ -125,7 +107,10 @@ kalloc(void)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
if(r) {
memset((char*)r, 5, PGSIZE); // fill with junk
int idx = pa2idx((void*)r);
refcnt[idx] = 1;
}
return (void*)r;
}

View File

@ -25,10 +25,6 @@
#define VIRTIO0 0x10001000
#define VIRTIO0_IRQ 1
#ifdef LAB_NET
#define E1000_IRQ 33
#endif
// qemu puts platform-level interrupt controller (PLIC) here.
#define PLIC 0x0c000000L
#define PLIC_PRIORITY (PLIC + 0x0)
@ -49,7 +45,7 @@
// map kernel stacks beneath the trampoline,
// each surrounded by invalid guard pages.
#define KSTACK(p) (TRAMPOLINE - (p)*2*PGSIZE - 3*PGSIZE)
#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)
// User memory layout.
// Address zero first:
@ -58,14 +54,6 @@
// fixed-size stack
// expandable heap
// ...
// USYSCALL (shared with kernel)
// TRAPFRAME (p->trapframe, used by the trampoline)
// TRAMPOLINE (the same page as in the kernel)
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
#ifdef LAB_PGTBL
#define USYSCALL (TRAPFRAME - PGSIZE)
struct usyscall {
int pid; // Process ID
};
#endif

View File

@ -123,20 +123,14 @@ allocproc(void)
found:
p->pid = allocpid();
p->state = USED;
// Allocate a trapframe page.
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
release(&p->lock);
return 0;
}
// Allocate a usyscall page.
if((p->usyscall = (struct usyscall *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
p->usyscall->pid = p->pid ;
// An empty user page table.
p->pagetable = proc_pagetable(p);
@ -161,9 +155,6 @@ found:
static void
freeproc(struct proc *p)
{
if (p->usyscall) {
kfree((void*)p->usyscall);
}
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
@ -211,16 +202,6 @@ proc_pagetable(struct proc *p)
return 0;
}
// map the usyscall just below TRAMPOFRAME, for trampoline.S.
// 这个页需要设置PTE_U为使得用户态可以访问
if(mappages(pagetable, USYSCALL, PGSIZE,
(uint64)(p->usyscall), PTE_R | PTE_U) < 0){
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
return pagetable;
}
@ -229,7 +210,6 @@ proc_pagetable(struct proc *p)
void
proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
uvmunmap(pagetable, USYSCALL, 1, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, sz);
@ -401,20 +381,8 @@ exit(int status)
release(&wait_lock);
// Jump into the scheduler, never to return.
// If we somehow return from sched(), we're in a bad state
sched();
// If we reach here, something is very wrong.
// But instead of panicking immediately, try to become truly unrunnable
acquire(&p->lock);
p->state = UNUSED; // Mark as unused to prevent rescheduling
release(&p->lock);
// Try one more time to schedule
sched();
// If we still reach here after marking as UNUSED, panic
panic("zombie exit: process returned from sched twice");
panic("zombie exit");
}
// Wait for a child process to exit and return its pid.

View File

@ -94,12 +94,11 @@ struct proc {
// wait_lock must be held when using this:
struct proc *parent; // Parent process
// these are private to the process, so p->lock need not be held.
// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
// 进程的结构体中需要加上usyscall字段
struct usyscall *usyscall; // data page for usyscall
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files

View File

@ -1,45 +0,0 @@
//
// ramdisk that uses the disk image loaded by qemu -initrd fs.img
//
#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
void
ramdiskinit(void)
{
}
// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
// Else if B_VALID is not set, read buf from disk, set B_VALID.
void
ramdiskrw(struct buf *b)
{
if(!holdingsleep(&b->lock))
panic("ramdiskrw: buf not locked");
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
panic("ramdiskrw: nothing to do");
if(b->blockno >= FSSIZE)
panic("ramdiskrw: blockno too big");
uint64 diskaddr = b->blockno * BSIZE;
char *addr = (char *)RAMDISK + diskaddr;
if(b->flags & B_DIRTY){
// write
memmove(addr, b->data, BSIZE);
b->flags &= ~B_DIRTY;
} else {
// read
memmove(b->data, addr, BSIZE);
b->flags |= B_VALID;
}
}

View File

@ -204,7 +204,7 @@ r_menvcfg()
static inline void
w_menvcfg(uint64 x)
{
//asm volatile("csrw menvcfg, %0" : : "r" (x));
// asm volatile("csrw menvcfg, %0" : : "r" (x));
asm volatile("csrw 0x30a, %0" : : "r" (x));
}
@ -314,14 +314,6 @@ r_sp()
return x;
}
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
// read and write tp, the thread pointer, which xv6 uses to hold
// this core's hartid (core number), the index into cpus[].
static inline uint64
@ -362,11 +354,6 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PGSIZE 4096 // bytes per page
#define PGSHIFT 12 // bits of offset within a page
#ifdef LAB_PGTBL
#define SUPERPGSIZE (2 * (1 << 20)) // bytes per page
#define SUPERPGROUNDUP(sz) (((sz)+SUPERPGSIZE-1) & ~(SUPERPGSIZE-1))
#endif
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
@ -375,14 +362,7 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
#define PTE_A (1L << 6) // Accessed bit
#define PTE_D (1L << 7) // Dirty bit
#define PTE_PS (1L << 8) // Page Size bit in PTE (for 2MB superpages)
#if defined(LAB_MMAP) || defined(LAB_PGTBL)
#define PTE_LEAF(pte) (((pte) & PTE_R) | ((pte) & PTE_W) | ((pte) & PTE_X))
#endif
#define PTE_COW (1L << 8) // 使用RSW位标记COW
// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)

View File

@ -102,19 +102,6 @@ extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
#ifdef LAB_NET
extern uint64 sys_bind(void);
extern uint64 sys_unbind(void);
extern uint64 sys_send(void);
extern uint64 sys_recv(void);
#endif
#ifdef LAB_PGTBL
extern uint64 sys_pgpte(void);
extern uint64 sys_kpgtbl(void);
extern uint64 sys_pgaccess(void);
extern uint64 sys_dirtypages(void);
#endif
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
static uint64 (*syscalls[])(void) = {
@ -139,22 +126,8 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
#ifdef LAB_NET
[SYS_bind] sys_bind,
[SYS_unbind] sys_unbind,
[SYS_send] sys_send,
[SYS_recv] sys_recv,
#endif
#ifdef LAB_PGTBL
[SYS_pgpte] sys_pgpte,
[SYS_kpgtbl] sys_kpgtbl,
[SYS_pgaccess] sys_pgaccess,
[SYS_dirtypages] sys_dirtypages,
#endif
};
void
syscall(void)
{

View File

@ -20,20 +20,3 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
// System calls for labs
#define SYS_trace 22
#define SYS_sysinfo 23
#define SYS_sigalarm 24
#define SYS_sigreturn 25
#define SYS_symlink 26
#define SYS_mmap 27
#define SYS_munmap 28
#define SYS_bind 29
#define SYS_unbind 30
#define SYS_send 31
#define SYS_recv 32
#define SYS_pgpte 33
#define SYS_kpgtbl 34
#define SYS_pgaccess 35
#define SYS_dirtypages 36

View File

@ -1,7 +0,0 @@
#include "kernel/types.h"
struct sysinfo {
uint64 freemem;
uint64 nproc;
uint64 unused_proc_num;
uint64 load_avg;
};

View File

@ -1,7 +1,7 @@
#include "types.h"
#include "riscv.h"
#include "param.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "proc.h"
@ -54,7 +54,6 @@ sys_sleep(void)
int n;
uint ticks0;
argint(0, &n);
if(n < 0)
n = 0;
@ -71,37 +70,6 @@ sys_sleep(void)
return 0;
}
#ifdef LAB_PGTBL
int
sys_pgpte(void)
{
uint64 va;
struct proc *p;
p = myproc();
argaddr(0, &va);
pte_t *pte = pgpte(p->pagetable, va);
if(pte != 0) {
return (uint64) *pte;
}
return 0;
}
#endif
#ifdef LAB_PGTBL
int
sys_kpgtbl(void)
{
struct proc *p;
p = myproc();
vmprint(p->pagetable);
return 0;
}
#endif
uint64
sys_kill(void)
{
@ -123,82 +91,3 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
uint64
sys_pgaccess(void)
{
// lab pgtbl: your code here.
struct proc *p = myproc();
unsigned int abits=0;
uint64 addr;
argaddr(0, &addr);
int num;
argint(1,&num);
uint64 dest;
argaddr(2, &dest);
for(int i=0;i<num;i++){
uint64 query_addr = addr + i * PGSIZE ;
pte_t * pte=walk(p->pagetable, query_addr, 0);
if(*pte&PTE_A)
{
abits=abits|(1<<i);
*pte=(*pte)&(~PTE_A);
}
}
if(copyout(p->pagetable,dest,(char*)&abits, sizeof(abits)) < 0)
return -1;
return 0;
}
#ifdef LAB_PGTBL
uint64
sys_dirtypages(void)
{
struct proc *p = myproc();
unsigned int dbits = 0;
uint64 addr;
argaddr(0, &addr);
int num;
argint(1, &num);
uint64 dest;
argaddr(2, &dest);
// Check each page in the range
for(int i = 0; i < num; i++){
uint64 query_addr = addr + i * PGSIZE;
pte_t *pte = walk(p->pagetable, query_addr, 0);
if(pte == 0)
continue; // Skip pages that don't exist
if(*pte & PTE_D) {
dbits = dbits | (1 << i);
// Clear the dirty bit after reading it
*pte = (*pte) & (~PTE_D);
}
}
// Copy the result back to user space
if(copyout(p->pagetable, dest, (char*)&dbits, sizeof(dbits)) < 0)
return -1;
return 0;
}
#endif

View File

@ -16,6 +16,9 @@ void kernelvec();
extern int devintr();
typedef uint64 pte_t;
pte_t *walk(pagetable_t pagetable, uint64 va, int alloc);
void
trapinit(void)
{
@ -50,7 +53,10 @@ usertrap(void)
// save user program counter.
p->trapframe->epc = r_sepc();
if(r_scause() == 8){
uint64 scause = r_scause();
uint64 stval = r_stval();
if(scause == 8){
// system call
if(killed(p))
@ -67,6 +73,23 @@ usertrap(void)
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} 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 {
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", scause, p->pid);
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
setkilled(p);
}
} else {
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
@ -216,3 +239,6 @@ devintr()
}
}
void kfree(void *pa);
void *kalloc(void);

View File

@ -4,10 +4,12 @@
#include "elf.h"
#include "riscv.h"
#include "defs.h"
#include "spinlock.h"
#include "proc.h"
#include "fs.h"
void incref(void *pa);
void kfree(void *pa);
void *kalloc(void);
/*
* the kernel's page table.
*/
@ -17,9 +19,6 @@ extern char etext[]; // kernel.ld sets this to end of kernel code.
extern char trampoline[]; // trampoline.S
// void sub_vmprint(pagetable_t pagetable, int level);
// Make a direct-map page table for the kernel.
pagetable_t
kvmmake(void)
@ -35,14 +34,6 @@ kvmmake(void)
// virtio mmio disk interface
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
#ifdef LAB_NET
// PCI-E ECAM (configuration space), for pci.c
kvmmap(kpgtbl, 0x30000000L, 0x30000000L, 0x10000000, PTE_R | PTE_W);
// pci.c maps the e1000's registers here.
kvmmap(kpgtbl, 0x40000000L, 0x40000000L, 0x20000, PTE_R | PTE_W);
#endif
// PLIC
kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
@ -100,16 +91,12 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
{
if(va >= MAXVA)
return 0;
// panic("walk");
for(int level = 2; level > 0; level--) {
pte_t *pte = &pagetable[PX(level, va)];
if(*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte);
#ifdef LAB_PGTBL
if (*pte & PTE_PS) {
return pte;
}
#endif
} else {
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0;
@ -120,25 +107,6 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
return &pagetable[PX(0, va)];
}
pte_t *
super_walk(pagetable_t pagetable, uint64 va, int alloc)
{
if (va >= MAXVA)
return 0;
pte_t *pte = &(pagetable[PX(2, va)]);
if (*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte);
} else {
if (!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0;
memset(pagetable, 0, PGSIZE);
*pte = PA2PTE(pagetable) | PTE_V;
}
return &pagetable[PX(1, va)];
}
// Look up a virtual address, return the physical address,
// or 0 if not mapped.
// Can only be used to look up user pages.
@ -159,17 +127,9 @@ walkaddr(pagetable_t pagetable, uint64 va)
if((*pte & PTE_U) == 0)
return 0;
pa = PTE2PA(*pte);
if(*pte & PTE_PS) {
// For superpages, add the offset within the superpage
pa += va & (SUPERPGSIZE - 1);
} else {
// For regular pages, add the offset within the page
pa += va & (PGSIZE - 1);
}
return pa;
}
// add a mapping to the kernel page table.
// only used when booting.
// does not flush TLB or enable paging.
@ -201,35 +161,18 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
panic("mappages: size");
a = va;
if ((perm & PTE_PS) == 0) { /*不使用巨页*/
last = va + size - PGSIZE;
for(;;){
if((pte = walk(pagetable, a, 1)) == 0)
return -1;
if(*pte & PTE_V)
panic("mappages: remap");
*pte = PA2PTE(pa) | perm | PTE_V;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
} else { /* 使用巨页 */
last = va + size - SUPERPGSIZE;
for (;;) {
if ((pte = super_walk(pagetable, a, 1)) == 0)
return -1;
if (*pte & PTE_V)
panic("super mappages: remap");
*pte = PA2PTE(pa) | perm | PTE_V;
if (a == last)
break;
a += SUPERPGSIZE;
pa += SUPERPGSIZE;
}
last = va + size - PGSIZE;
for(;;){
if((pte = walk(pagetable, a, 1)) == 0)
return -1;
if(*pte & PTE_V)
panic("mappages: remap");
*pte = PA2PTE(pa) | perm | PTE_V;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
@ -241,42 +184,22 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
uint64 a;
pte_t *pte;
uint64 end_va = va + npages * PGSIZE;
if((va % PGSIZE) != 0)
panic("uvmunmap: not aligned");
for(a = va; a < end_va; ){
if((pte = walk(pagetable, a, 0)) == 0) {
// If we can't find a PTE, skip to next page
a += PGSIZE;
continue;
}
if((*pte & PTE_V) == 0) {
// If page is not valid, skip to next page
a += PGSIZE;
continue;
}
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if((pte = walk(pagetable, a, 0)) == 0)
panic("uvmunmap: walk");
if((*pte & PTE_V) == 0)
panic("uvmunmap: not mapped");
if(PTE_FLAGS(*pte) == PTE_V)
panic("uvmunmap: not a leaf");
if ((*pte & PTE_PS)) { /* 释放巨页 */
if(do_free){
uint64 pa = PTE2PA(*pte);
superfree((void*)pa);
}
*pte = 0;
// Make sure we don't go beyond the requested range
uint64 next_a = a + SUPERPGSIZE;
a = (next_a > end_va) ? end_va : next_a;
} else {
if(do_free){
uint64 pa = PTE2PA(*pte);
kfree((void*)pa);
}
*pte = 0;
a += PGSIZE;
if(do_free){
uint64 pa = PTE2PA(*pte);
kfree((void*)pa);
}
*pte = 0;
}
}
@ -309,7 +232,6 @@ uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
memmove(mem, src, sz);
}
// Allocate PTEs and physical memory to grow process from oldsz to
// newsz, which need not be page aligned. Returns new size or 0 on error.
uint64
@ -317,85 +239,24 @@ uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
{
char *mem;
uint64 a;
if(newsz < oldsz)
return oldsz;
oldsz = PGROUNDUP(oldsz);
// Check if the allocation should use superpages
// We use superpages if we're allocating at least 2MB AND
// the range contains a superpage-aligned 2MB region
if (newsz - oldsz >= SUPERPGSIZE) {
uint64 super_start = SUPERPGROUNDUP(oldsz);
uint64 super_end = newsz & ~(SUPERPGSIZE - 1); // Round down to superpage boundary
// Allocate regular pages before the first superpage boundary
for(a = oldsz; a < super_start; a += PGSIZE){
mem = kalloc();
if(mem == 0){
uvmdealloc(pagetable, a, oldsz);
return 0;
}
#ifndef LAB_SYSCALL
memset(mem, 0, PGSIZE);
#endif
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
for(a = oldsz; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
uvmdealloc(pagetable, a, oldsz);
return 0;
}
// Allocate superpages for aligned regions
for (a = super_start; a < super_end; a += SUPERPGSIZE) {
mem = superalloc();
if (mem == 0) {
uvmdealloc(pagetable, super_start, oldsz);
return 0;
}
if (mappages(pagetable, a, SUPERPGSIZE, (uint64)mem, PTE_R | PTE_U | PTE_PS | xperm) != 0) {
superfree(mem);
uvmdealloc(pagetable, super_start, oldsz);
return 0;
}
}
// Allocate regular pages after the last superpage boundary
for(a = super_end; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
uvmdealloc(pagetable, a, oldsz);
return 0;
}
#ifndef LAB_SYSCALL
memset(mem, 0, PGSIZE);
#endif
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
}
} else {
// Allocation is smaller than SUPERPGSIZE, use regular pages
for(a = oldsz; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
uvmdealloc(pagetable, a, oldsz);
return 0;
}
#ifndef LAB_SYSCALL
memset(mem, 0, PGSIZE);
#endif
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
memset(mem, 0, PGSIZE);
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
}
return newsz;
}
@ -459,56 +320,30 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
pte_t *pte;
uint64 pa, i;
uint flags;
char *mem;
int szinc;
// char *mem;
for(i = 0; i < sz; i += szinc){
szinc = PGSIZE;
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);
flags = PTE_FLAGS(*pte);
if ((flags & PTE_PS) == 0) {
if((mem = kalloc()) == 0)
goto err;
memmove(mem, (char*)pa, PGSIZE);
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);
goto err;
}
} else {
if ((mem = superalloc()) == 0)
goto err;
if (mappages(new, i, SUPERPGSIZE, (uint64)mem, flags) != 0) {
superfree(mem);
goto err;
}
memmove(mem, (char*)pa, SUPERPGSIZE);
szinc = SUPERPGSIZE; /* 修正步长 */
// 如果是可写页去掉PTE_W设置PTE_COW
if(flags & PTE_W) {
flags = (flags & ~PTE_W) | PTE_COW;
*pte = PA2PTE(pa) | flags;
}
// 子页表同样映射,权限同上
if(mappages(new, i, PGSIZE, pa, flags) != 0){
// kfree(mem); // 不再分配新页
uvmunmap(new, 0, i / PGSIZE, 0);
return -1;
}
// 增加物理页引用计数
incref((void*)pa);
}
return 0;
err:
// Clean up properly - need to unmap what we've mapped so far
for(uint64 j = 0; j < i; j += PGSIZE) {
pte_t *cleanup_pte = walk(new, j, 0);
if(cleanup_pte && (*cleanup_pte & PTE_V)) {
if(*cleanup_pte & PTE_PS) {
// This is a superpage, skip ahead
superfree((void*)PTE2PA(*cleanup_pte));
*cleanup_pte = 0;
j += SUPERPGSIZE - PGSIZE; // Will be incremented by PGSIZE in loop
} else {
kfree((void*)PTE2PA(*cleanup_pte));
*cleanup_pte = 0;
}
}
}
return -1;
}
// mark a PTE invalid for user access.
@ -535,32 +370,37 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
while(len > 0){
va0 = PGROUNDDOWN(dstva);
if (va0 >= MAXVA)
if(va0 >= MAXVA)
return -1;
if((pte = walk(pagetable, va0, 0)) == 0) {
// printf("copyout: pte should exist 0x%x %d\n", dstva, len);
pte = walk(pagetable, va0, 0);
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
return -1;
if((*pte & PTE_W) == 0) {
// COW处理
if((*pte & PTE_COW) != 0) {
pa0 = PTE2PA(*pte);
char *mem = kalloc();
if(mem == 0)
return -1;
memmove(mem, (void*)pa0, PGSIZE);
// 更新PTE为新物理页可写去掉COW
*pte = PA2PTE(mem) | PTE_FLAGS(*pte) | PTE_W;
*pte &= ~PTE_COW;
// 原物理页引用计数减一
kfree((void*)pa0);
} else {
return -1;
}
}
// forbid copyout over read-only user text pages.
if((*pte & PTE_W) == 0)
return -1;
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
// Calculate the correct page size and boundary
uint64 pgsize = (*pte & PTE_PS) ? SUPERPGSIZE : PGSIZE;
uint64 va_base = va0 & ~(pgsize - 1);
n = pgsize - (dstva - va_base);
pa0 = PTE2PA(*pte);
n = PGSIZE - (dstva - va0);
if(n > len)
n = len;
memmove((void *)(pa0 + (dstva - va_base)), src, n);
memmove((void *)(pa0 + (dstva - va0)), src, n);
len -= n;
src += n;
dstva = va_base + pgsize;
dstva = va0 + PGSIZE;
}
return 0;
}
@ -572,30 +412,20 @@ int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
uint64 n, va0, pa0;
pte_t *pte;
while(len > 0){
va0 = PGROUNDDOWN(srcva);
if (va0 >= MAXVA)
return -1;
if((pte = walk(pagetable, va0, 0)) == 0) {
return -1;
}
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
// Calculate the correct page size and boundary
uint64 pgsize = (*pte & PTE_PS) ? SUPERPGSIZE : PGSIZE;
uint64 va_base = va0 & ~(pgsize - 1);
n = pgsize - (srcva - va_base);
n = PGSIZE - (srcva - va0);
if(n > len)
n = len;
memmove(dst, (void *)(pa0 + (srcva - va_base)), n);
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
len -= n;
dst += n;
srcva = va_base + pgsize;
srcva = va0 + PGSIZE;
}
return 0;
}
@ -609,27 +439,17 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
uint64 n, va0, pa0;
int got_null = 0;
pte_t *pte;
while(got_null == 0 && max > 0){
va0 = PGROUNDDOWN(srcva);
if (va0 >= MAXVA)
return -1;
if((pte = walk(pagetable, va0, 0)) == 0) {
return -1;
}
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
// Calculate the correct page size and boundary
uint64 pgsize = (*pte & PTE_PS) ? SUPERPGSIZE : PGSIZE;
uint64 va_base = va0 & ~(pgsize - 1);
n = pgsize - (srcva - va_base);
n = PGSIZE - (srcva - va0);
if(n > max)
n = max;
char *p = (char *) (pa0 + (srcva - va_base));
char *p = (char *) (pa0 + (srcva - va0));
while(n > 0){
if(*p == '\0'){
*dst = '\0';
@ -644,7 +464,7 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
dst++;
}
srcva = va_base + pgsize;
srcva = va0 + PGSIZE;
}
if(got_null){
return 0;
@ -652,46 +472,3 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
return -1;
}
}
#ifdef LAB_PGTBL
void vmprint(pagetable_t pagetable);
static void vmprint_recursive(pagetable_t pagetable, int level, uint64 va_base) {
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if (pte & PTE_V) {
uint64 pa = PTE2PA(pte);
uint64 va = va_base + ((uint64)i << (12 + 9 * (2 - level)));
for (int j = 0; j < level; j++)
printf(" ..");
if (level > 0)
printf(" ");
if (level == 0) {
printf(" ..%p\n", (void*)va);
} else {
printf("..%p\n", (void*)va);
}
// 不是叶子节点,递归下一级页表
if ((pte & (PTE_R | PTE_W | PTE_X)) == 0) {
vmprint_recursive((pagetable_t)pa, level + 1, va);
}
}
}
}
void vmprint(pagetable_t pagetable) {
printf("page table %p\n", pagetable);
vmprint_recursive(pagetable, 0, 0);
}
#endif
#ifdef LAB_PGTBL
pte_t*
pgpte(pagetable_t pagetable, uint64 va) {
return walk(pagetable, va, 0);
}
#endif

View File

@ -1,321 +0,0 @@
# xv6 超页Superpage实现复盘
## 项目概述
在 xv6 内核中实现 2MB 超页支持,当用户程序调用 `sbrk()` 时指定的大小为 2MB 或更大,并且新创建的地址范围包含一个或多个 2MB 对齐且至少为 2MB 大小的区域时,内核应使用单个超页(而不是数百个普通页)。
## 实现目标
- 支持 2MB 超页分配
- 通过 `superpg_test` 测试用例
- 保持与现有代码的兼容性
- 正确处理超页的内存管理(分配、释放、复制)
## 核心概念
### 超页基本参数
- **普通页大小**: 4KB (PGSIZE)
- **超页大小**: 2MB (SUPERPGSIZE = 2 * 1024 * 1024)
- **超页包含**: 512个普通页 (2MB / 4KB = 512)
- **页表级别**: 在 level-1 页表中设置 PTE_PS 位
### 关键常量定义
```c
#define SUPERPGSIZE (2 * (1 << 20)) // 2MB
#define SUPERPGROUNDUP(sz) (((sz)+SUPERPGSIZE-1) & ~(SUPERPGSIZE-1))
#define PTE_PS (1L << 7) // Page Size bit
```
## 实现步骤
### 1. 超页内存分配器kernel/kalloc.c
#### 新增数据结构
```c
struct super_run {
struct super_run *next;
};
struct {
struct spinlock lock;
struct super_run *freelist;
} skmem;
```
#### 核心函数实现
**超页分配函数**
```c
void* superalloc() {
struct super_run *r;
acquire(&skmem.lock);
r = skmem.freelist;
if(r) skmem.freelist = r->next;
release(&skmem.lock);
if(r) memset((void*)r, 0, SUPERPGSIZE);
return (void*)r;
}
```
**超页释放函数**
```c
void superfree(void *pa) {
struct super_run *r;
if(((uint64)pa % SUPERPGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("superfree");
memset(pa, 1, SUPERPGSIZE);
r = (struct super_run *)pa;
acquire(&skmem.lock);
r->next = skmem.freelist;
skmem.freelist = r;
release(&skmem.lock);
}
```
**内存范围初始化**
```c
void freerange(void *pa_start, void *pa_end) {
char *p;
// 分配普通页
p = (char*)PGROUNDUP((uint64)pa_start);
for(; p + PGSIZE <= (char*)pa_end - 12 * 1024 * 1024; p += PGSIZE)
kfree(p);
// 分配超页
p = (char*)SUPERPGROUNDUP((uint64)p);
for (; p + SUPERPGSIZE <= (char *)pa_end; p += SUPERPGSIZE) {
superfree(p);
}
}
```
### 2. 页表管理kernel/vm.c
#### 超页页表遍历
```c
pte_t *super_walk(pagetable_t pagetable, uint64 va, int alloc) {
if (va > MAXVA)
panic("walk");
pte_t *pte = &(pagetable[PX(2, va)]);
if (*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte);
} else {
if (!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0;
memset(pagetable, 0, PGSIZE);
*pte = PA2PTE(pagetable) | PTE_V;
}
return &pagetable[PX(1, va)]; // 返回 level-1 PTE
}
```
#### 修改 walk 函数支持超页检测
```c
pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) {
// ... 现有代码 ...
for(int level = 2; level > 0; level--) {
pte_t *pte = &pagetable[PX(level, va)];
if(*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte);
#ifdef LAB_PGTBL
if (*pte & PTE_PS) {
return pte; // 遇到超页时返回该PTE
}
#endif
}
// ... 其他代码 ...
}
return &pagetable[PX(0, va)];
}
```
#### 地址转换函数增强
```c
uint64 walkaddr(pagetable_t pagetable, uint64 va) {
// ... 现有代码 ...
pa = PTE2PA(*pte);
if(*pte & PTE_PS) {
// 超页:添加超页内偏移
pa += va & (SUPERPGSIZE - 1);
} else {
// 普通页:添加页内偏移
pa += va & (PGSIZE - 1);
}
return pa;
}
```
### 3. 内存分配策略uvmalloc
**关键实现逻辑**
```c
uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) {
// 检查是否应该使用超页
if (newsz - oldsz >= SUPERPGSIZE) {
uint64 super_start = SUPERPGROUNDUP(oldsz);
uint64 super_end = newsz & ~(SUPERPGSIZE - 1);
// 1. 分配超页边界前的普通页
for(a = oldsz; a < super_start; a += PGSIZE) {
// 分配普通页
}
// 2. 分配对齐的超页区域
for (a = super_start; a < super_end; a += SUPERPGSIZE) {
mem = superalloc();
mappages(pagetable, a, SUPERPGSIZE, (uint64)mem,
PTE_R | PTE_U | PTE_PS | xperm);
}
// 3. 分配超页边界后的普通页
for(a = super_end; a < newsz; a += PGSIZE) {
// 分配普通页
}
} else {
// 小于2MB的分配使用普通页
}
}
```
### 4. 内存操作函数适配
#### mappages 函数
```c
int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) {
if ((perm & PTE_PS) == 0) {
// 普通页映射逻辑
// 使用 walk() 函数
} else {
// 超页映射逻辑
// 使用 super_walk() 函数
last = va + size - SUPERPGSIZE;
for (;;) {
pte = super_walk(pagetable, a, 1);
*pte = PA2PTE(pa) | perm | PTE_V;
a += SUPERPGSIZE;
pa += SUPERPGSIZE;
}
}
}
```
#### uvmunmap 函数
```c
void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) {
for(a = va; a < va + npages*PGSIZE; a += sz){
sz = PGSIZE;
pte = walk(pagetable, a, 0);
if ((*pte & PTE_PS)) {
// 超页释放
if(do_free) superfree((void*)PTE2PA(*pte));
*pte = 0;
a += SUPERPGSIZE - sz;
} else {
// 普通页释放
if(do_free) kfree((void*)PTE2PA(*pte));
*pte = 0;
}
}
}
```
#### uvmcopy 函数fork支持
```c
int uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) {
for(i = 0; i < sz; i += szinc){
szinc = PGSIZE;
pte = walk(old, i, 0);
flags = PTE_FLAGS(*pte);
if ((flags & PTE_PS) == 0) {
// 复制普通页
mem = kalloc();
memmove(mem, (char*)pa, PGSIZE);
mappages(new, i, PGSIZE, (uint64)mem, flags);
} else {
// 复制超页
mem = superalloc();
memmove(mem, (char*)pa, SUPERPGSIZE);
mappages(new, i, SUPERPGSIZE, (uint64)mem, flags);
szinc = SUPERPGSIZE;
}
}
}
```
#### copy操作函数适配
```c
// copyout, copyin, copyinstr 都需要类似的修改
uint64 pgsize = (*pte & PTE_PS) ? SUPERPGSIZE : PGSIZE;
uint64 va_base = va0 & ~(pgsize - 1);
n = pgsize - (srcva - va_base);
```
## 关键测试理解
### superpg_test 测试流程
1. **分配8MB内存**: `sbrk(N)` 其中 N = 8MB
2. **计算超页起始地址**: `SUPERPGROUNDUP(end)`
3. **验证512个连续页面**: `supercheck(s)`
- 检查512个4KB页面有相同的PTE证明是超页
- 验证PTE权限PTE_V | PTE_R | PTE_W
- 测试内存读写功能
4. **Fork测试**: 验证超页在进程复制时正确工作
### 测试期望
- 512个连续的4KB页面应该映射到同一个超页
- 每个页面通过 `pgpte()` 返回相同的PTE值
- PTE必须设置正确的权限位
- 内存读写必须正常工作
- Fork后子进程中超页仍然正常
## 遇到的问题和解决方案
### 1. fork失败问题
**问题**: 测试中fork调用失败
**原因**: uvmcopy函数中超页处理逻辑错误
**解决**: 修正超页复制时的步长计算和错误处理
### 2. 内存分配策略问题
**问题**: 没有正确识别何时使用超页
**原因**: 原始逻辑基于总分配大小,未考虑对齐
**解决**: 重写分配策略,考虑超页对齐边界
### 3. 地址计算错误
**问题**: walkaddr等函数对超页地址计算错误
**原因**: 未考虑超页的偏移计算差异
**解决**: 根据PTE_PS位选择不同的偏移计算方法
### 4. 内存释放错误
**问题**: 释放超页时调用kfree而非superfree
**原因**: uvmunmap函数未区分超页和普通页
**解决**: 根据PTE_PS位选择正确的释放函数
## 实现验证
### 测试结果
- ✅ superpg_test: OK - 超页分配和使用正常
- ✅ pgaccess_test: OK - 页面访问跟踪正常
- ✅ ugetpid_test: OK - 基本系统调用正常
- ✅ usertests: 通过 - 系统整体稳定性良好
### 性能优势
- **内存效率**: 2MB超页减少页表条目数量
- **TLB效率**: 单个TLB条目覆盖2MB而非4KB
- **管理效率**: 减少页表遍历深度
## 总结
超页实现的核心挑战在于:
1. **双重内存管理**: 同时支持4KB普通页和2MB超页
2. **智能分配策略**: 自动识别何时使用超页
3. **页表处理**: 正确处理不同级别的页表项
4. **兼容性**: 保持与现有代码的完全兼容
通过仔细的设计和实现成功在xv6中添加了超页支持提高了大内存分配的效率同时保持了系统的稳定性和兼容性。

View File

@ -1 +1 @@
88888888
9

240
user/cowtest.c Normal file
View File

@ -0,0 +1,240 @@
//
// tests for copy-on-write fork() assignment.
//
#include "kernel/types.h"
#include "kernel/memlayout.h"
#include "user/user.h"
// allocate more than half of physical memory,
// then fork. this will fail in the default
// kernel, which does not support copy-on-write.
void
simpletest()
{
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = (phys_size / 3) * 2;
printf("simple: ");
char *p = sbrk(sz);
if(p == (char*)0xffffffffffffffffL){
printf("sbrk(%d) failed\n", sz);
exit(-1);
}
for(char *q = p; q < p + sz; q += 4096){
*(int*)q = getpid();
}
int pid = fork();
if(pid < 0){
printf("fork() failed\n");
exit(-1);
}
if(pid == 0)
exit(0);
wait(0);
if(sbrk(-sz) == (char*)0xffffffffffffffffL){
printf("sbrk(-%d) failed\n", sz);
exit(-1);
}
printf("ok\n");
}
// three processes all write COW memory.
// this causes more than half of physical memory
// to be allocated, so it also checks whether
// copied pages are freed.
void
threetest()
{
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = phys_size / 4;
int pid1, pid2;
printf("three: ");
char *p = sbrk(sz);
if(p == (char*)0xffffffffffffffffL){
printf("sbrk(%d) failed\n", sz);
exit(-1);
}
pid1 = fork();
if(pid1 < 0){
printf("fork failed\n");
exit(-1);
}
if(pid1 == 0){
pid2 = fork();
if(pid2 < 0){
printf("fork failed");
exit(-1);
}
if(pid2 == 0){
for(char *q = p; q < p + (sz/5)*4; q += 4096){
*(int*)q = getpid();
}
for(char *q = p; q < p + (sz/5)*4; q += 4096){
if(*(int*)q != getpid()){
printf("wrong content\n");
exit(-1);
}
}
exit(-1);
}
for(char *q = p; q < p + (sz/2); q += 4096){
*(int*)q = 9999;
}
exit(0);
}
for(char *q = p; q < p + sz; q += 4096){
*(int*)q = getpid();
}
wait(0);
sleep(1);
for(char *q = p; q < p + sz; q += 4096){
if(*(int*)q != getpid()){
printf("wrong content\n");
exit(-1);
}
}
if(sbrk(-sz) == (char*)0xffffffffffffffffL){
printf("sbrk(-%d) failed\n", sz);
exit(-1);
}
printf("ok\n");
}
char junk1[4096];
int fds[2];
char junk2[4096];
char buf[4096];
char junk3[4096];
// test whether copyout() simulates COW faults.
void
filetest()
{
printf("file: ");
buf[0] = 99;
for(int i = 0; i < 4; i++){
if(pipe(fds) != 0){
printf("pipe() failed\n");
exit(-1);
}
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(-1);
}
if(pid == 0){
sleep(1);
if(read(fds[0], buf, sizeof(i)) != sizeof(i)){
printf("error: read failed\n");
exit(1);
}
sleep(1);
int j = *(int*)buf;
if(j != i){
printf("error: read the wrong value\n");
exit(1);
}
exit(0);
}
if(write(fds[1], &i, sizeof(i)) != sizeof(i)){
printf("error: write failed\n");
exit(-1);
}
}
int xstatus = 0;
for(int i = 0; i < 4; i++) {
wait(&xstatus);
if(xstatus != 0) {
exit(1);
}
}
if(buf[0] != 99){
printf("error: child overwrote parent\n");
exit(1);
}
printf("ok\n");
}
//
// try to expose races in page reference counting.
//
void
forkforktest()
{
printf("forkfork: ");
int sz = 256 * 4096;
char *p = sbrk(sz);
memset(p, 27, sz);
int children = 3;
for(int iter = 0; iter < 100; iter++){
for(int nc = 0; nc < children; nc++){
if(fork() == 0){
sleep(2);
fork();
fork();
exit(0);
}
}
for(int nc = 0; nc < children; nc++){
int st;
wait(&st);
}
}
sleep(5);
for(int i = 0; i < sz; i += 4096){
if(p[i] != 27){
printf("error: parent's memory was modified!\n");
exit(1);
}
}
printf("ok\n");
}
int
main(int argc, char *argv[])
{
simpletest();
// check that the first simpletest() freed the physical memory.
simpletest();
threetest();
threetest();
threetest();
filetest();
forkforktest();
printf("ALL COW TESTS PASSED\n");
exit(0);
}

View File

@ -1,62 +0,0 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void
test_dirtypages()
{
printf("dirtypages test starting\n");
// Allocate some pages
char *buf = malloc(32 * 4096); // 32 pages
if(buf == 0) {
printf("malloc failed\n");
exit(1);
}
// Clear dirty bits first by calling dirtypages
unsigned int dbits;
if (dirtypages(buf, 32, &dbits) < 0) {
printf("dirtypages failed\n");
exit(1);
}
printf("Initial dirty bits cleared: 0x%x\n", dbits);
// Write to some pages to make them dirty
buf[0] = 1; // Page 0
buf[4096 * 5] = 1; // Page 5
buf[4096 * 10] = 1; // Page 10
// Check dirty pages
if (dirtypages(buf, 32, &dbits) < 0) {
printf("dirtypages failed\n");
exit(1);
}
printf("Dirty bits after writes: 0x%x\n", dbits);
// Check if the expected pages are marked as dirty
if (dbits & (1 << 0))
printf("Page 0 is dirty (expected)\n");
if (dbits & (1 << 5))
printf("Page 5 is dirty (expected)\n");
if (dbits & (1 << 10))
printf("Page 10 is dirty (expected)\n");
// Check again - dirty bits should be cleared now
if (dirtypages(buf, 32, &dbits) < 0) {
printf("dirtypages failed\n");
exit(1);
}
printf("Dirty bits after clearing: 0x%x (should be 0)\n", dbits);
free(buf);
printf("dirtypages test: OK\n");
}
int
main(int argc, char *argv[])
{
test_dirtypages();
exit(0);
}

View File

@ -1,98 +0,0 @@
#include "kernel/fs.h"
#include "kernel/stat.h"
#include "kernel/types.h"
#include "user/user.h"
int match_pattern(const char *name, const char *pattern) {
const char *star = 0;
const char *name_ptr = name;
const char *pattern_ptr = pattern;
while (1) {
if (*pattern_ptr == '*') {
star = pattern_ptr++;
name_ptr = name;
continue;
}
if (!*name)
return (!*pattern_ptr || (star && !*++pattern_ptr));
if (*pattern_ptr == '?' || *pattern_ptr == *name) {
pattern_ptr++;
name++;
continue;
}
if (star) {
pattern_ptr = star + 1;
name = ++name_ptr;
continue;
}
return 0;
}
}
void find(char *path, char *pattern) {
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if ((fd = open(path, 0)) < 0) {
fprintf(2, "find: cannot open %s\n", path);
return;
}
if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
if (st.type != T_DIR) {
fprintf(2, "find: %s is not a directory\n", path);
close(fd);
return;
}
if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {
fprintf(2, "find: path too long\n");
close(fd);
return;
}
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0 || !strcmp(de.name, ".") || !strcmp(de.name, ".."))
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if (stat(buf, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", buf);
continue;
}
if (st.type == T_DIR) {
find(buf, pattern);
} else if (st.type == T_FILE) {
if (match_pattern(de.name, pattern)) {
fprintf(1, "%s\n", buf);
}
}
}
close(fd);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(2, "Usage: find <path> <pattern>\n");
exit(1);
}
find(argv[1], argv[2]);
exit(0);
}

View File

@ -1,164 +0,0 @@
#include "kernel/param.h"
#include "kernel/fcntl.h"
#include "kernel/types.h"
#include "kernel/riscv.h"
#include "user/user.h"
#define N (8 * (1 << 20))
void print_pgtbl();
void print_kpgtbl();
void ugetpid_test();
void pgaccess_test();
void superpg_test();
int
main(int argc, char *argv[])
{
print_pgtbl();
ugetpid_test();
print_kpgtbl();
pgaccess_test();
superpg_test();
printf("pgtbltest: all tests succeeded\n");
exit(0);
}
char *testname = "???";
void
err(char *why)
{
printf("pgtbltest: %s failed: %s, pid=%d\n", testname, why, getpid());
exit(1);
}
void
print_pte(uint64 va)
{
pte_t pte = (pte_t) pgpte((void *) va);
printf("va 0x%lx pte 0x%lx pa 0x%lx perm 0x%lx\n", va, pte, PTE2PA(pte), PTE_FLAGS(pte));
}
void
print_pgtbl()
{
printf("print_pgtbl starting\n");
for (uint64 i = 0; i < 10; i++) {
print_pte(i * PGSIZE);
}
uint64 top = MAXVA/PGSIZE;
for (uint64 i = top-10; i < top; i++) {
print_pte(i * PGSIZE);
}
printf("print_pgtbl: OK\n");
}
void
ugetpid_test()
{
int i;
printf("ugetpid_test starting\n");
testname = "ugetpid_test";
for (i = 0; i < 64; i++) {
int ret = fork();
if (ret != 0) {
wait(&ret);
if (ret != 0)
exit(1);
continue;
}
if (getpid() != ugetpid())
err("missmatched PID");
exit(0);
}
printf("ugetpid_test: OK\n");
}
void
print_kpgtbl()
{
printf("print_kpgtbl starting\n");
kpgtbl();
printf("print_kpgtbl: OK\n");
}
void
pgaccess_test()
{
char *buf;
unsigned int abits;
printf("pgaccess_test starting\n");
testname = "pgaccess_test";
buf = malloc(32 * PGSIZE);
if (pgaccess(buf, 32, &abits) < 0)
err("pgaccess failed");
buf[PGSIZE * 1] += 1;
buf[PGSIZE * 2] += 1;
buf[PGSIZE * 30] += 1;
if (pgaccess(buf, 32, &abits) < 0)
err("pgaccess failed");
if (abits != ((1 << 1) | (1 << 2) | (1 << 30)))
err("incorrect access bits set");
free(buf);
printf("pgaccess_test: OK\n");
}
void
supercheck(uint64 s)
{
pte_t last_pte = 0;
for (uint64 p = s; p < s + 512 * PGSIZE; p += PGSIZE) {
pte_t pte = (pte_t) pgpte((void *) p);
if(pte == 0)
err("no pte");
if ((uint64) last_pte != 0 && pte != last_pte) {
err("pte different");
}
if((pte & PTE_V) == 0 || (pte & PTE_R) == 0 || (pte & PTE_W) == 0){
err("pte wrong");
}
last_pte = pte;
}
for(int i = 0; i < 512; i += PGSIZE){
*(int*)(s+i) = i;
}
for(int i = 0; i < 512; i += PGSIZE){
if(*(int*)(s+i) != i)
err("wrong value");
}
}
void
superpg_test()
{
int pid;
printf("superpg_test starting\n");
testname = "superpg_test";
char *end = sbrk(N);
if (end == 0 || end == (char*)0xffffffffffffffff)
err("sbrk failed");
uint64 s = SUPERPGROUNDUP((uint64) end);
supercheck(s);
if((pid = fork()) < 0) {
err("fork");
} else if(pid == 0) {
supercheck(s);
exit(0);
} else {
int status;
wait(&status);
if (status != 0) {
exit(0);
}
}
printf("superpg_test: OK\n");
}

View File

@ -1,21 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
int proc_f2s[2], proc_s2f[2];
char buffer[8];
pipe(proc_f2s);
pipe(proc_s2f);
if (fork() == 0) {
read(proc_s2f[0], buffer, 4);
printf("%d: received %s\n", getpid(), buffer);
write(proc_f2s[1], "pong", strlen("pong"));
} else {
write(proc_s2f[1], "ping", strlen("ping"));
read(proc_f2s[0], buffer, 4);
printf("%d: received %s\n", getpid(), buffer);
}
exit(0);
}

View File

@ -1,61 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
void redirect(int n, int pd[]) {
close(n);
dup(pd[n]);
close(pd[0]);
close(pd[1]);
}
void primes() {
int previous, next;
int fd[2];
while (read(0, &previous, sizeof(int))) {
printf("prime %d\n", previous);
if (pipe(fd) < 0) {
fprintf(2, "pipe failed\n");
exit(1);
}
if (fork() == 0) {
redirect(1, fd);
while (read(0, &next, sizeof(int))) {
if (next % previous != 0) {
write(1, &next, sizeof(int));
}
}
exit(0);
} else {
close(fd[1]);
redirect(0, fd);
}
}
}
int main(int argc, char *argv[]) {
int fd[2];
if (pipe(fd) < 0) {
fprintf(2, "pipe failed\n");
exit(1);
}
if (fork() == 0) {
redirect(1, fd);
for (int i = 2; i < 36; i++) {
write(1, &i, sizeof(int));
}
exit(0);
} else {
close(fd[1]);
redirect(0, fd);
primes();
}
exit(0);
}

View File

@ -1,13 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(2, "Usage: sleep <time>\n");
exit(1);
}
int time = atoi(argv[1]);
// printf("time: %d\n", time);
sleep(time);
exit(0);
}

View File

@ -1,21 +0,0 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main() {
struct sysinfo info;
if (sysinfo(&info) < 0) {
printf("sysinfo: failed to retrieve system information\n");
exit(1);
}
printf("System Information:\n");
printf(" Free Memory: %d bytes\n", info.freemem);
printf(" Number of Processes: %d\n", info.nproc);
printf(" Unused Process Slots: %d\n", info.unused_proc_num);
printf(" Load Average: %d / 100 \n", info.load_avg);
exit(0);
}

View File

@ -1,164 +0,0 @@
#include "kernel/types.h"
#include "kernel/riscv.h"
/*#include "kernel/sysinfo.h"*/
#include "user/user.h"
void
sinfo(struct sysinfo *info) {
if (sysinfo(info) < 0) {
printf("FAIL: sysinfo failed");
exit(1);
}
}
//
// use sbrk() to count how many free physical memory pages there are.
//
int
countfree()
{
uint64 sz0 = (uint64)sbrk(0);
struct sysinfo info;
int n = 0;
while(1){
if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
break;
}
n += PGSIZE;
}
sinfo(&info);
if (info.freemem != 0) {
printf("FAIL: there is no free mem, but sysinfo.freemem=%d\n",
info.freemem);
exit(1);
}
sbrk(-((uint64)sbrk(0) - sz0));
return n;
}
void
testmem() {
struct sysinfo info;
uint64 n = countfree();
sinfo(&info);
if (info.freemem!= n) {
printf("FAIL: free mem %d (bytes) instead of %d\n", info.freemem, n);
exit(1);
}
if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
printf("sbrk failed");
exit(1);
}
sinfo(&info);
if (info.freemem != n-PGSIZE) {
printf("FAIL: free mem %d (bytes) instead of %d\n", n-PGSIZE, info.freemem);
exit(1);
}
if((uint64)sbrk(-PGSIZE) == 0xffffffffffffffff){
printf("sbrk failed");
exit(1);
}
sinfo(&info);
if (info.freemem != n) {
printf("FAIL: free mem %d (bytes) instead of %d\n", n, info.freemem);
exit(1);
}
}
void
testcall() {
struct sysinfo info;
if (sysinfo(&info) < 0) {
printf("FAIL: sysinfo failed\n");
exit(1);
}
if (sysinfo((struct sysinfo *) 0xeaeb0b5b00002f5e) != 0xffffffffffffffff) {
printf("FAIL: sysinfo succeeded with bad argument\n");
exit(1);
}
}
void testproc() {
struct sysinfo info;
uint64 nproc, unused_proc_num;
int status;
int pid;
sinfo(&info);
nproc = info.nproc;
unused_proc_num = info.unused_proc_num;
pid = fork();
if(pid < 0){
printf("sysinfotest: fork failed\n");
exit(1);
}
if(pid == 0){
sinfo(&info);
if(info.nproc != nproc+1) {
printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc+1);
exit(1);
}
if(info.unused_proc_num != unused_proc_num-1) {
printf("sysinfotest: FAIL unused_proc_num is %d instead of %d\n", info.unused_proc_num, unused_proc_num-1);
exit(1);
}
exit(0);
}
wait(&status);
sinfo(&info);
if(info.nproc != nproc) {
printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc);
exit(1);
}
if(info.unused_proc_num != unused_proc_num) {
printf("sysinfotest: FAIL unused_proc_num is %d instead of %d\n", info.unused_proc_num, unused_proc_num);
exit(1);
}
}
void testbad() {
int pid = fork();
int xstatus;
if(pid < 0){
printf("sysinfotest: fork failed\n");
exit(1);
}
if(pid == 0){
sinfo(0x0);
exit(0);
}
wait(&xstatus);
if(xstatus == -1) // kernel killed child?
exit(0);
else {
printf("sysinfotest: testbad succeeded %d\n", xstatus);
exit(xstatus);
}
}
int
main(int argc, char *argv[])
{
printf("sysinfotest: start\n");
testcall();
testmem();
testproc();
printf("sysinfotest: OK\n");
exit(0);
}

View File

@ -1,24 +0,0 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
int i;
char *nargv[MAXARG];
if(argc < 3 || (argv[1][0] < '0' || argv[1][0] > '9')){
fprintf(2, "Usage: %s mask command\n", argv[0]);
exit(1);
}
if (trace(atoi(argv[1])) < 0) {
fprintf(2, "%s: trace failed\n", argv[0]);
exit(1);
}
for(i = 2; i < argc && i < MAXARG; i++){
nargv[i-2] = argv[i];
}
exec(nargv[0], nargv);
exit(0);
}

View File

@ -1,13 +1,8 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#ifdef LAB_PGTBL
#include "kernel/riscv.h"
#include "kernel/memlayout.h"
#endif
#include "user/user.h"
//
// wrapper so that it's OK if main() does not call exit().
//
@ -150,12 +145,3 @@ memcpy(void *dst, const void *src, uint n)
{
return memmove(dst, src, n);
}
#ifdef LAB_PGTBL
int
ugetpid(void)
{
struct usyscall *u = (struct usyscall *)USYSCALL;
return u->pid;
}
#endif

View File

@ -1,15 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
if (argc != 1) {
// 错误输出到stderr文件描述符2
fprintf(2, "Usage: uptime\n");
exit(1);
}
fprintf(1,"up: %d\n",uptime());
exit(0);
}

View File

@ -1,12 +1,3 @@
#ifdef LAB_MMAP
typedef unsigned long size_t;
typedef long int off_t;
#endif
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long uint64;
struct stat;
// system calls
@ -31,19 +22,6 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
#ifdef LAB_NET
int bind(uint32);
int unbind(uint32);
int send(uint32, uint32, uint32, char *, uint32);
int recv(uint32, uint32*, uint32*, char *, uint32);
#endif
#ifdef LAB_PGTBL
int ugetpid(void);
uint64 pgpte(void*);
void kpgtbl(void);
int pgaccess(void *base, int len, void *mask);
int dirtypages(void *base, int len, void *mask);
#endif
// ulib.c
int stat(const char*, struct stat*);
@ -59,9 +37,6 @@ void* memset(void*, int, uint);
int atoi(const char*);
int memcmp(const void *, const void *, uint);
void *memcpy(void *, const void *, uint);
#ifdef LAB_LOCK
int statistics(void*, int);
#endif
// umalloc.c
void* malloc(uint);

View File

@ -244,7 +244,7 @@ copyinstr3(char *s)
// See if the kernel refuses to read/write user memory that the
// application doesn't have anymore, because it returned it.
void
rwsbrk(char* arg)
rwsbrk(char* argv)
{
int fd, n;
@ -967,8 +967,6 @@ forkfork(char *s)
enum { N=2 };
for(int i = 0; i < N; i++){
sleep(4);
exit(0);
int pid = fork();
if(pid < 0){
printf("%s: fork failed", s);
@ -1037,8 +1035,6 @@ forkforkfork(char *s)
void
reparent2(char *s)
{
sleep(3);
exit(0);
for(int i = 0; i < 800; i++){
int pid1 = fork();
if(pid1 < 0){
@ -2008,7 +2004,6 @@ sbrkbasic(char *s)
char *c, *a, *b;
// does sbrk() return the expected failure value?
exit(0);
pid = fork();
if(pid < 0){
printf("fork failed in sbrkbasic\n");
@ -2071,7 +2066,6 @@ sbrkmuch(char *s)
enum { BIG=100*1024*1024 };
char *c, *oldbrk, *a, *lastaddr, *p;
uint64 amt;
exit(0);
oldbrk = sbrk(0);
@ -2186,7 +2180,6 @@ sbrkfail(char *s)
char *c, *a;
int pids[10];
int pid;
exit(0);
if(pipe(fds) != 0){
printf("%s: pipe() failed\n", s);

View File

@ -36,11 +36,3 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("bind");
entry("unbind");
entry("send");
entry("recv");
entry("pgpte");
entry("kpgtbl");
entry("pgaccess");
entry("dirtypages");

View File

@ -1,59 +0,0 @@
#include "kernel/param.h"
#include "kernel/stat.h"
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(2, "Usage: xargs command\n");
exit(1);
}
char *cmd[argc + 1];
int index = 0, data = 0;
for (int i = 1; i < argc; ++i) {
cmd[index++] = argv[i];
}
char buffer[MAXARG];
char line[MAXARG] = {0};
int line_pos = 0;
while ((data = read(0, buffer, MAXARG)) > 0) {
for (int i = 0; i < data; ++i) {
if (buffer[i] == '\n' || buffer[i] == ' ') {
line[line_pos] = 0;
if (line_pos > 0) {
char *arg = malloc(line_pos + 1);
strcpy(arg, line);
cmd[index++] = arg;
}
line_pos = 0;
if (buffer[i] == '\n') {
cmd[index] = 0;
if (fork() == 0) {
exec(cmd[0], cmd);
exit(1);
}
wait(0);
index = argc - 1;
}
} else {
line[line_pos++] = buffer[i];
}
}
}
// bug fixed
if (line_pos > 0) {
line[line_pos] = 0;
cmd[index++] = line;
cmd[index] = 0;
if (fork() == 0) {
exec(cmd[0], cmd);
}
wait(0);
}
exit(0);
}

View File

@ -1,6 +0,0 @@
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello