Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8bae855c5 | |||
| 66657d1ca0 | |||
| bc8921a77f | |||
| c6fa25504f | |||
| 7c42fbed17 | |||
| 70ee668b9b | |||
| c7c962ffef | |||
| 4c08583743 | |||
| b63d0c9147 | |||
| 028cf61d61 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,3 +24,5 @@ ph
|
|||||||
barrier
|
barrier
|
||||||
/lab-*.json
|
/lab-*.json
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
|||||||
10
Makefile
10
Makefile
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# To compile and run with a lab solution, set the lab name in conf/lab.mk
|
# 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
|
# (e.g., LAB=util). Run make grade to test solution with the lab's
|
||||||
# grade script (e.g., grade-lab-util).
|
# grade script (e.g., grade-lab-util).
|
||||||
@ -84,7 +85,7 @@ LD = $(TOOLPREFIX)ld
|
|||||||
OBJCOPY = $(TOOLPREFIX)objcopy
|
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||||
OBJDUMP = $(TOOLPREFIX)objdump
|
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
|
ifdef LAB
|
||||||
LABUPPER = $(shell echo $(LAB) | tr a-z A-Z)
|
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
|
$(OBJDUMP) -S $U/_uthread > $U/uthread.asm
|
||||||
|
|
||||||
ph: notxv6/ph.c
|
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
|
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
|
endif
|
||||||
|
|
||||||
ifeq ($(LAB),pgtbl)
|
ifeq ($(LAB),pgtbl)
|
||||||
UPROGS += \
|
UPROGS += \
|
||||||
$U/_pgtbltest\
|
$U/_pgtbltest
|
||||||
$U/_dirtypagestest
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(LAB),lock)
|
ifeq ($(LAB),lock)
|
||||||
|
|||||||
46
README.md
46
README.md
@ -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".
|
||||||
|
|||||||
@ -1,153 +0,0 @@
|
|||||||
### 背景信息
|
|
||||||
- **页面表结构**:
|
|
||||||
- 页面大小(`PGSIZE`):通常为 4KB(4096 字节)。
|
|
||||||
- 最大虚拟地址(`MAXVA`):在 Sv39 模式下为 2^39(512GB)。
|
|
||||||
- 页面表条目(PTE):包含物理地址(PA)、权限位(读/写/执行等)以及有效位(Valid bit)。
|
|
||||||
- 三级页面表:PML4(顶级),L2(中间级),L1(最低级),每个页面表包含 512 条目(2^9)。
|
|
||||||
- 虚拟地址(VA):39 位,分为 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 的条目,以及其下级页面表的最后几个条目(509–511)。
|
|
||||||
- 对应虚拟地址接近 `MAXVA`(`0x3fffffd` 到 `0x3ffffff`)。
|
|
||||||
- 输出格式:
|
|
||||||
- 每行表示一个页面表条目,格式为:
|
|
||||||
```
|
|
||||||
[索引]: pte [PTE值] pa [物理地址] [va 虚拟地址 flags 权限]
|
|
||||||
```
|
|
||||||
- 缩进(`..`)表示页面表层级:顶级(无缩进)、L2(`..`)、L1(`.. ..`)。
|
|
||||||
- 仅 L1 页面表条目包含虚拟地址(`va`)和权限标志(`flags`)。
|
|
||||||
|
|
||||||
#### 2. **页面表条目解析**
|
|
||||||
页面表条目(PTE)格式(RISC-V Sv39):
|
|
||||||
- **PTE 结构**:64 位,包含:
|
|
||||||
- 物理页面号(PPN):44 位(高 44 位,右移 10 位得到物理页面地址)。
|
|
||||||
- 权限位:V(Valid,有效)、R(Read,读)、W(Write,写)、X(Execute,执行)、U(User,用户态可访问)。
|
|
||||||
- 其他标志:G(Global)、A(Accessed)、D(Dirty)等。
|
|
||||||
- **物理地址(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`,表示 Valid(V=1)。
|
|
||||||
- **L2 页面表(索引 0)**:
|
|
||||||
- `..0: pte 0x0000000021fd2001 pa 0x0000000087f48000`
|
|
||||||
- PTE 值:`0x21fd2001`。
|
|
||||||
- 物理地址:`0x87f48000`(L1 页面表地址)。
|
|
||||||
- 标志:Valid(V=1)。
|
|
||||||
- **L1 页面表(索引 0–3)**:
|
|
||||||
- `.. ..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 页面表地址)。
|
|
||||||
- 标志:Valid(V=1)。
|
|
||||||
- **L2 页面表(索引 511)**:
|
|
||||||
- `..511: pte 0x0000000021fd2c01 pa 0x0000000087f4b000`
|
|
||||||
- PTE 值:`0x21fd2c01`。
|
|
||||||
- 物理地址:`0x87f4b000`(L1 页面表地址)。
|
|
||||||
- 标志:Valid(V=1)。
|
|
||||||
- **L1 页面表(索引 509–511)**:
|
|
||||||
- `.. ..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 个页面**:
|
|
||||||
- 输出显示索引 509–511(虚拟地址 `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 个页面(509–511),可能是因为只有这些页面有有效映射。
|
|
||||||
- **缺失的 `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
@ -1 +1 @@
|
|||||||
LAB=pgtbl
|
LAB=cow
|
||||||
|
|||||||
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 标志,保证了内存的正确共享与释放。
|
||||||
|
- 通过边界检查和优雅失败,保证了系统健壮性。
|
||||||
|
|
||||||
|
本实验加深了对操作系统内存管理、页表机制和异常处理的理解。
|
||||||
59
grade-lab-cow
Normal file
59
grade-lab-cow
Normal 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()
|
||||||
@ -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()
|
|
||||||
@ -1,7 +1,3 @@
|
|||||||
#ifdef LAB_MMAP
|
|
||||||
typedef unsigned long size_t;
|
|
||||||
typedef long int off_t;
|
|
||||||
#endif
|
|
||||||
struct buf;
|
struct buf;
|
||||||
struct context;
|
struct context;
|
||||||
struct file;
|
struct file;
|
||||||
@ -121,10 +117,6 @@ void initlock(struct spinlock*, char*);
|
|||||||
void release(struct spinlock*);
|
void release(struct spinlock*);
|
||||||
void push_off(void);
|
void push_off(void);
|
||||||
void pop_off(void);
|
void pop_off(void);
|
||||||
int atomic_read4(int *addr);
|
|
||||||
#ifdef LAB_LOCK
|
|
||||||
void freelock(struct spinlock*);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// sleeplock.c
|
// sleeplock.c
|
||||||
void acquiresleep(struct sleeplock*);
|
void acquiresleep(struct sleeplock*);
|
||||||
@ -181,17 +173,6 @@ uint64 walkaddr(pagetable_t, uint64);
|
|||||||
int copyout(pagetable_t, uint64, char *, uint64);
|
int copyout(pagetable_t, uint64, char *, uint64);
|
||||||
int copyin(pagetable_t, char *, uint64, uint64);
|
int copyin(pagetable_t, char *, uint64, uint64);
|
||||||
int copyinstr(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
|
// plic.c
|
||||||
void plicinit(void);
|
void plicinit(void);
|
||||||
@ -206,31 +187,3 @@ void virtio_disk_intr(void);
|
|||||||
|
|
||||||
// number of elements in fixed-size array
|
// number of elements in fixed-size array
|
||||||
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
|
#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
|
|
||||||
|
|||||||
@ -128,10 +128,6 @@ exec(char *path, char **argv)
|
|||||||
p->trapframe->sp = sp; // initial stack pointer
|
p->trapframe->sp = sp; // initial stack pointer
|
||||||
proc_freepagetable(oldpagetable, oldsz);
|
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)
|
return argc; // this ends up in a0, the first argument to main(argc, argv)
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
|
|||||||
@ -9,6 +9,12 @@
|
|||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
#include "defs.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);
|
void freerange(void *pa_start, void *pa_end);
|
||||||
|
|
||||||
extern char end[]; // first address after kernel.
|
extern char end[]; // first address after kernel.
|
||||||
@ -23,46 +29,25 @@ struct {
|
|||||||
struct run *freelist;
|
struct run *freelist;
|
||||||
} kmem;
|
} kmem;
|
||||||
|
|
||||||
struct super_run {
|
void incref(void *pa) {
|
||||||
struct super_run *next;
|
int idx = pa2idx(pa);
|
||||||
};
|
refcnt[idx]++;
|
||||||
|
|
||||||
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 decref(void *pa) {
|
||||||
void* superalloc() {
|
int idx = pa2idx(pa);
|
||||||
struct super_run *r;
|
refcnt[idx]--;
|
||||||
acquire(&skmem.lock);
|
}
|
||||||
r = skmem.freelist;
|
int getref(void *pa) {
|
||||||
if(r) skmem.freelist = r->next;
|
int idx = pa2idx(pa);
|
||||||
release(&skmem.lock);
|
return refcnt[idx];
|
||||||
if(r) memset((void*)r, 0, SUPERPGSIZE);
|
|
||||||
return (void*)r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kinit()
|
kinit()
|
||||||
{
|
{
|
||||||
initlock(&kmem.lock, "kmem");
|
initlock(&kmem.lock, "kmem");
|
||||||
initlock(&skmem.lock, "skmem");
|
for(int i = 0; i < NPAGE; i++)
|
||||||
|
refcnt[i] = 0;
|
||||||
freerange(end, (void*)PHYSTOP);
|
freerange(end, (void*)PHYSTOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,22 +56,9 @@ freerange(void *pa_start, void *pa_end)
|
|||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
p = (char*)PGROUNDUP((uint64)pa_start);
|
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);
|
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,
|
// Free the page of physical memory pointed at by pa,
|
||||||
// which normally should have been returned by a
|
// 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)
|
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
|
||||||
panic("kfree");
|
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.
|
// Fill with junk to catch dangling refs.
|
||||||
memset(pa, 1, PGSIZE);
|
memset(pa, 1, PGSIZE);
|
||||||
|
|
||||||
@ -125,7 +107,10 @@ kalloc(void)
|
|||||||
kmem.freelist = r->next;
|
kmem.freelist = r->next;
|
||||||
release(&kmem.lock);
|
release(&kmem.lock);
|
||||||
|
|
||||||
if(r)
|
if(r) {
|
||||||
memset((char*)r, 5, PGSIZE); // fill with junk
|
memset((char*)r, 5, PGSIZE); // fill with junk
|
||||||
|
int idx = pa2idx((void*)r);
|
||||||
|
refcnt[idx] = 1;
|
||||||
|
}
|
||||||
return (void*)r;
|
return (void*)r;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,10 +25,6 @@
|
|||||||
#define VIRTIO0 0x10001000
|
#define VIRTIO0 0x10001000
|
||||||
#define VIRTIO0_IRQ 1
|
#define VIRTIO0_IRQ 1
|
||||||
|
|
||||||
#ifdef LAB_NET
|
|
||||||
#define E1000_IRQ 33
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// qemu puts platform-level interrupt controller (PLIC) here.
|
// qemu puts platform-level interrupt controller (PLIC) here.
|
||||||
#define PLIC 0x0c000000L
|
#define PLIC 0x0c000000L
|
||||||
#define PLIC_PRIORITY (PLIC + 0x0)
|
#define PLIC_PRIORITY (PLIC + 0x0)
|
||||||
@ -49,7 +45,7 @@
|
|||||||
|
|
||||||
// map kernel stacks beneath the trampoline,
|
// map kernel stacks beneath the trampoline,
|
||||||
// each surrounded by invalid guard pages.
|
// 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.
|
// User memory layout.
|
||||||
// Address zero first:
|
// Address zero first:
|
||||||
@ -58,14 +54,6 @@
|
|||||||
// fixed-size stack
|
// fixed-size stack
|
||||||
// expandable heap
|
// expandable heap
|
||||||
// ...
|
// ...
|
||||||
// USYSCALL (shared with kernel)
|
|
||||||
// TRAPFRAME (p->trapframe, used by the trampoline)
|
// TRAPFRAME (p->trapframe, used by the trampoline)
|
||||||
// TRAMPOLINE (the same page as in the kernel)
|
// TRAMPOLINE (the same page as in the kernel)
|
||||||
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
||||||
#ifdef LAB_PGTBL
|
|
||||||
#define USYSCALL (TRAPFRAME - PGSIZE)
|
|
||||||
|
|
||||||
struct usyscall {
|
|
||||||
int pid; // Process ID
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -123,20 +123,14 @@ allocproc(void)
|
|||||||
|
|
||||||
found:
|
found:
|
||||||
p->pid = allocpid();
|
p->pid = allocpid();
|
||||||
|
p->state = USED;
|
||||||
|
|
||||||
// Allocate a trapframe page.
|
// Allocate a trapframe page.
|
||||||
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
|
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);
|
freeproc(p);
|
||||||
release(&p->lock);
|
release(&p->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
p->usyscall->pid = p->pid ;
|
|
||||||
|
|
||||||
// An empty user page table.
|
// An empty user page table.
|
||||||
p->pagetable = proc_pagetable(p);
|
p->pagetable = proc_pagetable(p);
|
||||||
@ -161,9 +155,6 @@ found:
|
|||||||
static void
|
static void
|
||||||
freeproc(struct proc *p)
|
freeproc(struct proc *p)
|
||||||
{
|
{
|
||||||
if (p->usyscall) {
|
|
||||||
kfree((void*)p->usyscall);
|
|
||||||
}
|
|
||||||
if(p->trapframe)
|
if(p->trapframe)
|
||||||
kfree((void*)p->trapframe);
|
kfree((void*)p->trapframe);
|
||||||
p->trapframe = 0;
|
p->trapframe = 0;
|
||||||
@ -211,16 +202,6 @@ proc_pagetable(struct proc *p)
|
|||||||
return 0;
|
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;
|
return pagetable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +210,6 @@ proc_pagetable(struct proc *p)
|
|||||||
void
|
void
|
||||||
proc_freepagetable(pagetable_t pagetable, uint64 sz)
|
proc_freepagetable(pagetable_t pagetable, uint64 sz)
|
||||||
{
|
{
|
||||||
uvmunmap(pagetable, USYSCALL, 1, 0);
|
|
||||||
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
|
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
|
||||||
uvmunmap(pagetable, TRAPFRAME, 1, 0);
|
uvmunmap(pagetable, TRAPFRAME, 1, 0);
|
||||||
uvmfree(pagetable, sz);
|
uvmfree(pagetable, sz);
|
||||||
@ -401,20 +381,8 @@ exit(int status)
|
|||||||
release(&wait_lock);
|
release(&wait_lock);
|
||||||
|
|
||||||
// Jump into the scheduler, never to return.
|
// Jump into the scheduler, never to return.
|
||||||
// If we somehow return from sched(), we're in a bad state
|
|
||||||
sched();
|
sched();
|
||||||
|
panic("zombie exit");
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a child process to exit and return its pid.
|
// Wait for a child process to exit and return its pid.
|
||||||
|
|||||||
@ -94,12 +94,11 @@ struct proc {
|
|||||||
|
|
||||||
// wait_lock must be held when using this:
|
// wait_lock must be held when using this:
|
||||||
struct proc *parent; // Parent process
|
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 kstack; // Virtual address of kernel stack
|
||||||
uint64 sz; // Size of process memory (bytes)
|
uint64 sz; // Size of process memory (bytes)
|
||||||
pagetable_t pagetable; // User page table
|
pagetable_t pagetable; // User page table
|
||||||
// 进程的结构体中需要加上usyscall字段
|
|
||||||
struct usyscall *usyscall; // data page for usyscall
|
|
||||||
struct trapframe *trapframe; // data page for trampoline.S
|
struct trapframe *trapframe; // data page for trampoline.S
|
||||||
struct context context; // swtch() here to run process
|
struct context context; // swtch() here to run process
|
||||||
struct file *ofile[NOFILE]; // Open files
|
struct file *ofile[NOFILE]; // Open files
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -204,7 +204,7 @@ r_menvcfg()
|
|||||||
static inline void
|
static inline void
|
||||||
w_menvcfg(uint64 x)
|
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));
|
asm volatile("csrw 0x30a, %0" : : "r" (x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,14 +314,6 @@ r_sp()
|
|||||||
return x;
|
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
|
// read and write tp, the thread pointer, which xv6 uses to hold
|
||||||
// this core's hartid (core number), the index into cpus[].
|
// this core's hartid (core number), the index into cpus[].
|
||||||
static inline uint64
|
static inline uint64
|
||||||
@ -362,11 +354,6 @@ typedef uint64 *pagetable_t; // 512 PTEs
|
|||||||
#define PGSIZE 4096 // bytes per page
|
#define PGSIZE 4096 // bytes per page
|
||||||
#define PGSHIFT 12 // bits of offset within a 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 PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
|
||||||
#define PGROUNDDOWN(a) (((a)) & ~(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_W (1L << 2)
|
||||||
#define PTE_X (1L << 3)
|
#define PTE_X (1L << 3)
|
||||||
#define PTE_U (1L << 4) // user can access
|
#define PTE_U (1L << 4) // user can access
|
||||||
#define PTE_A (1L << 6) // Accessed bit
|
#define PTE_COW (1L << 8) // 使用RSW位标记COW
|
||||||
#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
|
|
||||||
|
|
||||||
// shift a physical address to the right place for a PTE.
|
// shift a physical address to the right place for a PTE.
|
||||||
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
||||||
|
|||||||
@ -102,19 +102,6 @@ extern uint64 sys_link(void);
|
|||||||
extern uint64 sys_mkdir(void);
|
extern uint64 sys_mkdir(void);
|
||||||
extern uint64 sys_close(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
|
// An array mapping syscall numbers from syscall.h
|
||||||
// to the function that handles the system call.
|
// to the function that handles the system call.
|
||||||
static uint64 (*syscalls[])(void) = {
|
static uint64 (*syscalls[])(void) = {
|
||||||
@ -139,22 +126,8 @@ static uint64 (*syscalls[])(void) = {
|
|||||||
[SYS_link] sys_link,
|
[SYS_link] sys_link,
|
||||||
[SYS_mkdir] sys_mkdir,
|
[SYS_mkdir] sys_mkdir,
|
||||||
[SYS_close] sys_close,
|
[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
|
void
|
||||||
syscall(void)
|
syscall(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,20 +20,3 @@
|
|||||||
#define SYS_link 19
|
#define SYS_link 19
|
||||||
#define SYS_mkdir 20
|
#define SYS_mkdir 20
|
||||||
#define SYS_close 21
|
#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
|
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
#include "kernel/types.h"
|
|
||||||
struct sysinfo {
|
|
||||||
uint64 freemem;
|
|
||||||
uint64 nproc;
|
|
||||||
uint64 unused_proc_num;
|
|
||||||
uint64 load_avg;
|
|
||||||
};
|
|
||||||
113
kernel/sysproc.c
113
kernel/sysproc.c
@ -1,7 +1,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
#include "param.h"
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
#include "memlayout.h"
|
#include "memlayout.h"
|
||||||
#include "spinlock.h"
|
#include "spinlock.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
@ -54,7 +54,6 @@ sys_sleep(void)
|
|||||||
int n;
|
int n;
|
||||||
uint ticks0;
|
uint ticks0;
|
||||||
|
|
||||||
|
|
||||||
argint(0, &n);
|
argint(0, &n);
|
||||||
if(n < 0)
|
if(n < 0)
|
||||||
n = 0;
|
n = 0;
|
||||||
@ -71,37 +70,6 @@ sys_sleep(void)
|
|||||||
return 0;
|
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
|
uint64
|
||||||
sys_kill(void)
|
sys_kill(void)
|
||||||
{
|
{
|
||||||
@ -123,82 +91,3 @@ sys_uptime(void)
|
|||||||
release(&tickslock);
|
release(&tickslock);
|
||||||
return xticks;
|
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
|
|
||||||
|
|||||||
@ -16,6 +16,9 @@ void kernelvec();
|
|||||||
|
|
||||||
extern int devintr();
|
extern int devintr();
|
||||||
|
|
||||||
|
typedef uint64 pte_t;
|
||||||
|
pte_t *walk(pagetable_t pagetable, uint64 va, int alloc);
|
||||||
|
|
||||||
void
|
void
|
||||||
trapinit(void)
|
trapinit(void)
|
||||||
{
|
{
|
||||||
@ -50,7 +53,10 @@ usertrap(void)
|
|||||||
// save user program counter.
|
// save user program counter.
|
||||||
p->trapframe->epc = r_sepc();
|
p->trapframe->epc = r_sepc();
|
||||||
|
|
||||||
if(r_scause() == 8){
|
uint64 scause = r_scause();
|
||||||
|
uint64 stval = r_stval();
|
||||||
|
|
||||||
|
if(scause == 8){
|
||||||
// system call
|
// system call
|
||||||
|
|
||||||
if(killed(p))
|
if(killed(p))
|
||||||
@ -67,6 +73,23 @@ usertrap(void)
|
|||||||
syscall();
|
syscall();
|
||||||
} else if((which_dev = devintr()) != 0){
|
} else if((which_dev = devintr()) != 0){
|
||||||
// ok
|
// 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 {
|
} else {
|
||||||
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
|
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());
|
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
|
||||||
@ -216,3 +239,6 @@ devintr()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kfree(void *pa);
|
||||||
|
void *kalloc(void);
|
||||||
|
|
||||||
|
|||||||
385
kernel/vm.c
385
kernel/vm.c
@ -4,10 +4,12 @@
|
|||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "spinlock.h"
|
|
||||||
#include "proc.h"
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
|
||||||
|
void incref(void *pa);
|
||||||
|
void kfree(void *pa);
|
||||||
|
void *kalloc(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the kernel's page table.
|
* 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
|
extern char trampoline[]; // trampoline.S
|
||||||
|
|
||||||
// void sub_vmprint(pagetable_t pagetable, int level);
|
|
||||||
|
|
||||||
|
|
||||||
// Make a direct-map page table for the kernel.
|
// Make a direct-map page table for the kernel.
|
||||||
pagetable_t
|
pagetable_t
|
||||||
kvmmake(void)
|
kvmmake(void)
|
||||||
@ -35,14 +34,6 @@ kvmmake(void)
|
|||||||
// virtio mmio disk interface
|
// virtio mmio disk interface
|
||||||
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
|
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
|
// PLIC
|
||||||
kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
|
kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
|
||||||
|
|
||||||
@ -100,16 +91,12 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
|
|||||||
{
|
{
|
||||||
if(va >= MAXVA)
|
if(va >= MAXVA)
|
||||||
return 0;
|
return 0;
|
||||||
|
// panic("walk");
|
||||||
|
|
||||||
for(int level = 2; level > 0; level--) {
|
for(int level = 2; level > 0; level--) {
|
||||||
pte_t *pte = &pagetable[PX(level, va)];
|
pte_t *pte = &pagetable[PX(level, va)];
|
||||||
if(*pte & PTE_V) {
|
if(*pte & PTE_V) {
|
||||||
pagetable = (pagetable_t)PTE2PA(*pte);
|
pagetable = (pagetable_t)PTE2PA(*pte);
|
||||||
#ifdef LAB_PGTBL
|
|
||||||
if (*pte & PTE_PS) {
|
|
||||||
return pte;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
|
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -120,25 +107,6 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
|
|||||||
return &pagetable[PX(0, va)];
|
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,
|
// Look up a virtual address, return the physical address,
|
||||||
// or 0 if not mapped.
|
// or 0 if not mapped.
|
||||||
// Can only be used to look up user pages.
|
// Can only be used to look up user pages.
|
||||||
@ -159,17 +127,9 @@ walkaddr(pagetable_t pagetable, uint64 va)
|
|||||||
if((*pte & PTE_U) == 0)
|
if((*pte & PTE_U) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
pa = PTE2PA(*pte);
|
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;
|
return pa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// add a mapping to the kernel page table.
|
// add a mapping to the kernel page table.
|
||||||
// only used when booting.
|
// only used when booting.
|
||||||
// does not flush TLB or enable paging.
|
// 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");
|
panic("mappages: size");
|
||||||
|
|
||||||
a = va;
|
a = va;
|
||||||
|
last = va + size - PGSIZE;
|
||||||
if ((perm & PTE_PS) == 0) { /*不使用巨页*/
|
for(;;){
|
||||||
last = va + size - PGSIZE;
|
if((pte = walk(pagetable, a, 1)) == 0)
|
||||||
for(;;){
|
return -1;
|
||||||
if((pte = walk(pagetable, a, 1)) == 0)
|
if(*pte & PTE_V)
|
||||||
return -1;
|
panic("mappages: remap");
|
||||||
if(*pte & PTE_V)
|
*pte = PA2PTE(pa) | perm | PTE_V;
|
||||||
panic("mappages: remap");
|
if(a == last)
|
||||||
*pte = PA2PTE(pa) | perm | PTE_V;
|
break;
|
||||||
if(a == last)
|
a += PGSIZE;
|
||||||
break;
|
pa += PGSIZE;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,42 +184,22 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
|
|||||||
{
|
{
|
||||||
uint64 a;
|
uint64 a;
|
||||||
pte_t *pte;
|
pte_t *pte;
|
||||||
uint64 end_va = va + npages * PGSIZE;
|
|
||||||
|
|
||||||
if((va % PGSIZE) != 0)
|
if((va % PGSIZE) != 0)
|
||||||
panic("uvmunmap: not aligned");
|
panic("uvmunmap: not aligned");
|
||||||
|
|
||||||
for(a = va; a < end_va; ){
|
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
|
||||||
if((pte = walk(pagetable, a, 0)) == 0) {
|
if((pte = walk(pagetable, a, 0)) == 0)
|
||||||
// If we can't find a PTE, skip to next page
|
panic("uvmunmap: walk");
|
||||||
a += PGSIZE;
|
if((*pte & PTE_V) == 0)
|
||||||
continue;
|
panic("uvmunmap: not mapped");
|
||||||
}
|
|
||||||
if((*pte & PTE_V) == 0) {
|
|
||||||
// If page is not valid, skip to next page
|
|
||||||
a += PGSIZE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(PTE_FLAGS(*pte) == PTE_V)
|
if(PTE_FLAGS(*pte) == PTE_V)
|
||||||
panic("uvmunmap: not a leaf");
|
panic("uvmunmap: not a leaf");
|
||||||
|
if(do_free){
|
||||||
if ((*pte & PTE_PS)) { /* 释放巨页 */
|
uint64 pa = PTE2PA(*pte);
|
||||||
if(do_free){
|
kfree((void*)pa);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
*pte = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +232,6 @@ uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
|
|||||||
memmove(mem, src, sz);
|
memmove(mem, src, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Allocate PTEs and physical memory to grow process from oldsz to
|
// 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.
|
// newsz, which need not be page aligned. Returns new size or 0 on error.
|
||||||
uint64
|
uint64
|
||||||
@ -317,85 +239,24 @@ uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
|
|||||||
{
|
{
|
||||||
char *mem;
|
char *mem;
|
||||||
uint64 a;
|
uint64 a;
|
||||||
|
|
||||||
if(newsz < oldsz)
|
if(newsz < oldsz)
|
||||||
return oldsz;
|
return oldsz;
|
||||||
|
|
||||||
oldsz = PGROUNDUP(oldsz);
|
oldsz = PGROUNDUP(oldsz);
|
||||||
|
for(a = oldsz; a < newsz; a += PGSIZE){
|
||||||
// Check if the allocation should use superpages
|
mem = kalloc();
|
||||||
// We use superpages if we're allocating at least 2MB AND
|
if(mem == 0){
|
||||||
// the range contains a superpage-aligned 2MB region
|
uvmdealloc(pagetable, a, oldsz);
|
||||||
if (newsz - oldsz >= SUPERPGSIZE) {
|
return 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
memset(mem, 0, PGSIZE);
|
||||||
// Allocate superpages for aligned regions
|
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
|
||||||
for (a = super_start; a < super_end; a += SUPERPGSIZE) {
|
kfree(mem);
|
||||||
mem = superalloc();
|
uvmdealloc(pagetable, a, oldsz);
|
||||||
if (mem == 0) {
|
return 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newsz;
|
return newsz;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,56 +320,30 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
|||||||
pte_t *pte;
|
pte_t *pte;
|
||||||
uint64 pa, i;
|
uint64 pa, i;
|
||||||
uint flags;
|
uint flags;
|
||||||
char *mem;
|
// char *mem;
|
||||||
int szinc;
|
|
||||||
|
|
||||||
for(i = 0; i < sz; i += szinc){
|
for(i = 0; i < sz; i += PGSIZE){
|
||||||
szinc = PGSIZE;
|
|
||||||
if((pte = walk(old, i, 0)) == 0)
|
if((pte = walk(old, i, 0)) == 0)
|
||||||
panic("uvmcopy: pte should exist");
|
panic("uvmcopy: pte should exist");
|
||||||
if((*pte & PTE_V) == 0)
|
if((*pte & PTE_V) == 0)
|
||||||
panic("uvmcopy: page not present");
|
panic("uvmcopy: page not present");
|
||||||
pa = PTE2PA(*pte);
|
pa = PTE2PA(*pte);
|
||||||
flags = PTE_FLAGS(*pte);
|
flags = PTE_FLAGS(*pte);
|
||||||
|
// 如果是可写页,去掉PTE_W,设置PTE_COW
|
||||||
if ((flags & PTE_PS) == 0) {
|
if(flags & PTE_W) {
|
||||||
if((mem = kalloc()) == 0)
|
flags = (flags & ~PTE_W) | PTE_COW;
|
||||||
goto err;
|
*pte = PA2PTE(pa) | flags;
|
||||||
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; /* 修正步长 */
|
|
||||||
}
|
}
|
||||||
|
// 子页表同样映射,权限同上
|
||||||
|
if(mappages(new, i, PGSIZE, pa, flags) != 0){
|
||||||
|
// kfree(mem); // 不再分配新页
|
||||||
|
uvmunmap(new, 0, i / PGSIZE, 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// 增加物理页引用计数
|
||||||
|
incref((void*)pa);
|
||||||
}
|
}
|
||||||
return 0;
|
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.
|
// mark a PTE invalid for user access.
|
||||||
@ -535,32 +370,37 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
|||||||
|
|
||||||
while(len > 0){
|
while(len > 0){
|
||||||
va0 = PGROUNDDOWN(dstva);
|
va0 = PGROUNDDOWN(dstva);
|
||||||
if (va0 >= MAXVA)
|
if(va0 >= MAXVA)
|
||||||
return -1;
|
return -1;
|
||||||
if((pte = walk(pagetable, va0, 0)) == 0) {
|
pte = walk(pagetable, va0, 0);
|
||||||
// printf("copyout: pte should exist 0x%x %d\n", dstva, len);
|
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
|
||||||
return -1;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pa0 = PTE2PA(*pte);
|
||||||
// forbid copyout over read-only user text pages.
|
n = PGSIZE - (dstva - va0);
|
||||||
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);
|
|
||||||
if(n > len)
|
if(n > len)
|
||||||
n = len;
|
n = len;
|
||||||
memmove((void *)(pa0 + (dstva - va_base)), src, n);
|
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
||||||
|
|
||||||
len -= n;
|
len -= n;
|
||||||
src += n;
|
src += n;
|
||||||
dstva = va_base + pgsize;
|
dstva = va0 + PGSIZE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -572,30 +412,20 @@ int
|
|||||||
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
||||||
{
|
{
|
||||||
uint64 n, va0, pa0;
|
uint64 n, va0, pa0;
|
||||||
pte_t *pte;
|
|
||||||
|
|
||||||
while(len > 0){
|
while(len > 0){
|
||||||
va0 = PGROUNDDOWN(srcva);
|
va0 = PGROUNDDOWN(srcva);
|
||||||
if (va0 >= MAXVA)
|
|
||||||
return -1;
|
|
||||||
if((pte = walk(pagetable, va0, 0)) == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pa0 = walkaddr(pagetable, va0);
|
pa0 = walkaddr(pagetable, va0);
|
||||||
if(pa0 == 0)
|
if(pa0 == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
n = PGSIZE - (srcva - va0);
|
||||||
// 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);
|
|
||||||
if(n > len)
|
if(n > len)
|
||||||
n = len;
|
n = len;
|
||||||
memmove(dst, (void *)(pa0 + (srcva - va_base)), n);
|
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
|
||||||
|
|
||||||
len -= n;
|
len -= n;
|
||||||
dst += n;
|
dst += n;
|
||||||
srcva = va_base + pgsize;
|
srcva = va0 + PGSIZE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -609,27 +439,17 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
|||||||
{
|
{
|
||||||
uint64 n, va0, pa0;
|
uint64 n, va0, pa0;
|
||||||
int got_null = 0;
|
int got_null = 0;
|
||||||
pte_t *pte;
|
|
||||||
|
|
||||||
while(got_null == 0 && max > 0){
|
while(got_null == 0 && max > 0){
|
||||||
va0 = PGROUNDDOWN(srcva);
|
va0 = PGROUNDDOWN(srcva);
|
||||||
if (va0 >= MAXVA)
|
|
||||||
return -1;
|
|
||||||
if((pte = walk(pagetable, va0, 0)) == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pa0 = walkaddr(pagetable, va0);
|
pa0 = walkaddr(pagetable, va0);
|
||||||
if(pa0 == 0)
|
if(pa0 == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
n = PGSIZE - (srcva - va0);
|
||||||
// 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);
|
|
||||||
if(n > max)
|
if(n > max)
|
||||||
n = max;
|
n = max;
|
||||||
|
|
||||||
char *p = (char *) (pa0 + (srcva - va_base));
|
char *p = (char *) (pa0 + (srcva - va0));
|
||||||
while(n > 0){
|
while(n > 0){
|
||||||
if(*p == '\0'){
|
if(*p == '\0'){
|
||||||
*dst = '\0';
|
*dst = '\0';
|
||||||
@ -644,7 +464,7 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
|||||||
dst++;
|
dst++;
|
||||||
}
|
}
|
||||||
|
|
||||||
srcva = va_base + pgsize;
|
srcva = va0 + PGSIZE;
|
||||||
}
|
}
|
||||||
if(got_null){
|
if(got_null){
|
||||||
return 0;
|
return 0;
|
||||||
@ -652,46 +472,3 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
|||||||
return -1;
|
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
|
|
||||||
|
|||||||
@ -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中添加了超页支持,提高了大内存分配的效率,同时保持了系统的稳定性和兼容性。
|
|
||||||
240
user/cowtest.c
Normal file
240
user/cowtest.c
Normal 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);
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
98
user/find.c
98
user/find.c
@ -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);
|
|
||||||
}
|
|
||||||
164
user/pgtbltest.c
164
user/pgtbltest.c
@ -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");
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
13
user/sleep.c
13
user/sleep.c
@ -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);
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
24
user/trace.c
24
user/trace.c
@ -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);
|
|
||||||
}
|
|
||||||
14
user/ulib.c
14
user/ulib.c
@ -1,13 +1,8 @@
|
|||||||
#include "kernel/types.h"
|
#include "kernel/types.h"
|
||||||
#include "kernel/stat.h"
|
#include "kernel/stat.h"
|
||||||
#include "kernel/fcntl.h"
|
#include "kernel/fcntl.h"
|
||||||
#ifdef LAB_PGTBL
|
|
||||||
#include "kernel/riscv.h"
|
|
||||||
#include "kernel/memlayout.h"
|
|
||||||
#endif
|
|
||||||
#include "user/user.h"
|
#include "user/user.h"
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// wrapper so that it's OK if main() does not call exit().
|
// 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);
|
return memmove(dst, src, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LAB_PGTBL
|
|
||||||
int
|
|
||||||
ugetpid(void)
|
|
||||||
{
|
|
||||||
struct usyscall *u = (struct usyscall *)USYSCALL;
|
|
||||||
return u->pid;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
25
user/user.h
25
user/user.h
@ -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;
|
struct stat;
|
||||||
|
|
||||||
// system calls
|
// system calls
|
||||||
@ -31,19 +22,6 @@ int getpid(void);
|
|||||||
char* sbrk(int);
|
char* sbrk(int);
|
||||||
int sleep(int);
|
int sleep(int);
|
||||||
int uptime(void);
|
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
|
// ulib.c
|
||||||
int stat(const char*, struct stat*);
|
int stat(const char*, struct stat*);
|
||||||
@ -59,9 +37,6 @@ void* memset(void*, int, uint);
|
|||||||
int atoi(const char*);
|
int atoi(const char*);
|
||||||
int memcmp(const void *, const void *, uint);
|
int memcmp(const void *, const void *, uint);
|
||||||
void *memcpy(void *, const void *, uint);
|
void *memcpy(void *, const void *, uint);
|
||||||
#ifdef LAB_LOCK
|
|
||||||
int statistics(void*, int);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// umalloc.c
|
// umalloc.c
|
||||||
void* malloc(uint);
|
void* malloc(uint);
|
||||||
|
|||||||
@ -244,7 +244,7 @@ copyinstr3(char *s)
|
|||||||
// See if the kernel refuses to read/write user memory that the
|
// See if the kernel refuses to read/write user memory that the
|
||||||
// application doesn't have anymore, because it returned it.
|
// application doesn't have anymore, because it returned it.
|
||||||
void
|
void
|
||||||
rwsbrk(char* arg)
|
rwsbrk(char* argv)
|
||||||
{
|
{
|
||||||
int fd, n;
|
int fd, n;
|
||||||
|
|
||||||
@ -967,8 +967,6 @@ forkfork(char *s)
|
|||||||
enum { N=2 };
|
enum { N=2 };
|
||||||
|
|
||||||
for(int i = 0; i < N; i++){
|
for(int i = 0; i < N; i++){
|
||||||
sleep(4);
|
|
||||||
exit(0);
|
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if(pid < 0){
|
if(pid < 0){
|
||||||
printf("%s: fork failed", s);
|
printf("%s: fork failed", s);
|
||||||
@ -1037,8 +1035,6 @@ forkforkfork(char *s)
|
|||||||
void
|
void
|
||||||
reparent2(char *s)
|
reparent2(char *s)
|
||||||
{
|
{
|
||||||
sleep(3);
|
|
||||||
exit(0);
|
|
||||||
for(int i = 0; i < 800; i++){
|
for(int i = 0; i < 800; i++){
|
||||||
int pid1 = fork();
|
int pid1 = fork();
|
||||||
if(pid1 < 0){
|
if(pid1 < 0){
|
||||||
@ -2008,7 +2004,6 @@ sbrkbasic(char *s)
|
|||||||
char *c, *a, *b;
|
char *c, *a, *b;
|
||||||
|
|
||||||
// does sbrk() return the expected failure value?
|
// does sbrk() return the expected failure value?
|
||||||
exit(0);
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if(pid < 0){
|
if(pid < 0){
|
||||||
printf("fork failed in sbrkbasic\n");
|
printf("fork failed in sbrkbasic\n");
|
||||||
@ -2071,7 +2066,6 @@ sbrkmuch(char *s)
|
|||||||
enum { BIG=100*1024*1024 };
|
enum { BIG=100*1024*1024 };
|
||||||
char *c, *oldbrk, *a, *lastaddr, *p;
|
char *c, *oldbrk, *a, *lastaddr, *p;
|
||||||
uint64 amt;
|
uint64 amt;
|
||||||
exit(0);
|
|
||||||
|
|
||||||
oldbrk = sbrk(0);
|
oldbrk = sbrk(0);
|
||||||
|
|
||||||
@ -2186,7 +2180,6 @@ sbrkfail(char *s)
|
|||||||
char *c, *a;
|
char *c, *a;
|
||||||
int pids[10];
|
int pids[10];
|
||||||
int pid;
|
int pid;
|
||||||
exit(0);
|
|
||||||
|
|
||||||
if(pipe(fds) != 0){
|
if(pipe(fds) != 0){
|
||||||
printf("%s: pipe() failed\n", s);
|
printf("%s: pipe() failed\n", s);
|
||||||
|
|||||||
@ -36,11 +36,3 @@ entry("getpid");
|
|||||||
entry("sbrk");
|
entry("sbrk");
|
||||||
entry("sleep");
|
entry("sleep");
|
||||||
entry("uptime");
|
entry("uptime");
|
||||||
entry("bind");
|
|
||||||
entry("unbind");
|
|
||||||
entry("send");
|
|
||||||
entry("recv");
|
|
||||||
entry("pgpte");
|
|
||||||
entry("kpgtbl");
|
|
||||||
entry("pgaccess");
|
|
||||||
entry("dirtypages");
|
|
||||||
|
|||||||
59
user/xargs.c
59
user/xargs.c
@ -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);
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
mkdir a
|
|
||||||
echo hello > a/b
|
|
||||||
mkdir c
|
|
||||||
echo hello > c/b
|
|
||||||
echo hello > b
|
|
||||||
find . b | xargs grep hello
|
|
||||||
Reference in New Issue
Block a user