diff --git a/Makefile b/Makefile index 7bed292..af4c4ca 100644 --- a/Makefile +++ b/Makefile @@ -213,8 +213,7 @@ endif ifeq ($(LAB),traps) UPROGS += \ $U/_call\ - $U/_bttest\ - $U/_alarmtest + $U/_bttest endif ifeq ($(LAB),lazy) diff --git a/README b/README deleted file mode 100644 index f583201..0000000 --- a/README +++ /dev/null @@ -1,46 +0,0 @@ -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". diff --git a/README.md b/README.md index e69de29..f583201 100644 --- a/README.md +++ b/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". diff --git a/alarmtest_tests.md b/alarmtest_tests.md deleted file mode 100644 index eebc1e5..0000000 --- a/alarmtest_tests.md +++ /dev/null @@ -1,91 +0,0 @@ -# alarmtest.c 测试 - -## test0: 单次调用测试 - -**函数**: `test0()` - -**目的**: 测试内核是否至少调用了一次 alarm 处理程序。 - -**描述**: - -- 设置 alarm 每 2 个时钟周期触发一次。 - -- 在一个长循环中检查 `count` 是否被处理程序修改。 - -- 如果 `count > 0`,测试通过;否则失败。 - ---- - -## test1: 多次调用与寄存器恢复测试 - -**函数**: `test1()` - -**目的**: 测试内核是否多次调用 alarm 处理程序,并确保处理程序返回后程序继续从中断点执行,且寄存器值未被破坏。 - -**描述**: - -- 设置 alarm 每 2 个时钟周期触发一次。 - -- 在循环中调用 `foo()` 函数,同时检查 `count` 是否达到 10。 - -- 验证 `foo()` 的调用次数是否与循环计数一致。 - -- 如果 `count < 10` 或寄存器值不一致,测试失败;否则通过。 - ---- - -## test2: 防止重入调用 - -**函数**: `test2()` - -**目的**: 测试内核是否防止 alarm 处理程序的重入调用。 - -**描述**: - -- 设置 alarm 每 2 个时钟周期触发一次,处理程序为 `slow_handler()`。 - -- 在子进程中运行一个长循环,检查 `count` 是否被修改。 - -- 如果 `count > 1`,测试失败;否则通过。 - ---- - -## test3: 寄存器 a0 的完整性测试 - -**函数**: `test3()` - -**目的**: 测试从 `sys_sigreturn()` 返回时,寄存器 `a0` 的值是否被修改。 - -**描述**: - -- 设置 alarm 每 1 个时钟周期触发一次,处理程序为 `dummy_handler()`。 - -- 在触发 alarm 后检查寄存器 `a0` 的值是否保持为初始值 `0xac`。 - -- 如果 `a0` 被修改,测试失败;否则通过。 - ---- - -## 处理程序说明 - -### `periodic()` - -- 增加 `count` 值并打印 "alarm!"。 - -- 调用 `sigreturn()` 返回。 - -### `slow_handler()` - -- 增加 `count` 值并打印 "alarm!"。 - -- 如果 `count > 1`,测试失败并退出。 - -- 运行一个长循环以模拟慢速处理程序。 - -- 调用 `sigreturn()` 返回。 - -### `dummy_handler()` - -- 立即卸载自身并调用 `sigreturn()` 返回。 - ---- diff --git a/answers-traps.txt b/answers-traps.txt deleted file mode 100644 index ad562c6..0000000 --- a/answers-traps.txt +++ /dev/null @@ -1,67 +0,0 @@ - 问题 1: 哪些寄存器包含函数的参数?例如,在 main 对 printf 的调用中,哪个寄存器包含 13? - -在 RISC-V 调用约定中,函数参数通常通过寄存器 a0 到 a7 传递。 - - 对于函数 g(int x) 和 f(int x) ,它们的参数 x 都通过寄存器 a0 传递。 - 在 main 对 printf 的调用中, printf("y=%d\n", 13) 语句中,数字 13 将包含在寄存器 a1 中。 printf 的第一个参数(格式字符串的地址)将包含在寄存器 a0 中。 - - 问题 2: 在 main 的汇编代码中,对函数 f 的调用在哪里?对 g 的调用在哪里?(提示:编译器可以内联函数。) - -由于编译器优化, f 和 g 函数在 main 函数中被内联了。这意味着它们的实际代码直接插入到 main 函数的调用点,而不是通过 jal 或 jalr 指令进行函数调用。 - -我们可以通过观察 main 函数的汇编代码来验证这一点。在 main 函数中,你会看到对 f 和 g 的操作直接在 main 的代码流中执行,而没有显式的 jal 或 jalr 到 的地址。 - - 问题 3: 函数 printf 位于哪个地址? - - printf 函数不在 call.asm 文件提供的汇编代码中。 call.asm 似乎只包含用户定义的函数和一些与内存分配相关的代码。 printf 是一个标准库函数,它的地址会在程序链接时确定,并且通常在运行时动态加载。因此,我们无法从 call.asm 的反汇编代码中直接找到 printf 的地址。 - - 问题 4: 在 main 中 jalr 到 printf 之后的寄存器 ra 中有什么值? - -在 main 函数中 jalr 到 printf 之后,寄存器 ra (返回地址寄存器) 将包含紧跟在 jalr 指令之后的指令的地址。这个地址是 printf 函数执行完毕后应该返回到的 main 函数中的位置。 - - 问题 5: 运行以下代码。无符号 int i = 0x00646c72; printf(“H%x Wo%s”, 57616, (字符 ) &i); 输出是什么? - - i = 0x00646c72 - printf("H%x Wo%s", 57616, (char ) &i) - -输出: H0e110 Wo rld - -解释: - - "H" 和 "Wo" 是直接打印的字符串。 - %x 对应 57616 。 57616 的十六进制是 0xE110 。 - %s 对应 (char ) &i 。由于 RISC-V 是 little-endian,内存中 i 的字节顺序是 72 6C 64 00 。 - 0x72 是字符 'r' - 0x6C 是字符 'l' - 0x64 是字符 'd' - 0x00 是字符串终止符 - 因此, %s 会打印 "rld"。 - - 问题 6: 如果 RISC-V 是 big-endian,您会将 i 设置为什么 i 才能产生相同的输出?是否需要将 57616 更改为其他值? - - 如果 RISC-V 是 big-endian: - 为了使 %s 打印 "rld",我们需要将 i 设置为 0x726c6400 。在 big-endian 系统中,高位字节存储在低内存地址,所以 0x72 会在最低地址,然后是 0x6c , 0x64 ,最后是 0x00 。 - - 是否需要将 57616 更改为其他值? - 不需要。 57616 是一个整数值,无论系统是 little-endian 还是 big-endian,它的数值表示是不变的。 %x 格式说明符只是将其打印为十六进制。字节序只影响多字节数据类型(如 int )在内存中的存储方式,而不影响其数值。 - - 问题 7: 在下面的代码中, 'y=' 后面会打印什么?(注意:答案不是具体值。为什么会这样? - - c -printf("x=%d y=%d", 3); - - -输出: y= 后面会打印一个不确定的值(垃圾值)。 - -为什么会这样: - - printf 是一个变参函数,它根据格式字符串中的格式说明符来确定需要从栈中取出多少个参数。 - - 格式字符串是 "x=%d y=%d" 。 - 这意味着 printf 期望有两个 %d 对应的整数参数。 - 但是,在实际调用中,只提供了一个参数 3 。 - -因此: - -1. printf 会从栈中读取第一个整数参数来填充 x=%d ,这个值就是 3 。 -2. 然后, printf 会尝试读取第二个整数参数来填充 y=%d 。由于调用时只提供了 3 这一个参数, printf 会从栈上读取一个任意的、未初始化的内存位置的值,这个值是不可预测的(垃圾值)。 diff --git a/conf/lab.mk b/conf/lab.mk index 2b9988d..629f978 100644 --- a/conf/lab.mk +++ b/conf/lab.mk @@ -1 +1 @@ -LAB=traps +LAB=cow diff --git a/grade-lab-cow b/grade-lab-cow new file mode 100644 index 0000000..c51363b --- /dev/null +++ b/grade-lab-cow @@ -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() diff --git a/grade-lab-traps b/grade-lab-traps deleted file mode 100644 index 10613d3..0000000 --- a/grade-lab-traps +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import subprocess -from gradelib import * - -r = Runner(save("xv6.out")) - -@test(5, "answers-traps.txt") -def test_answers(): - # just a simple sanity check, will be graded manually - check_answers("answers-traps.txt") - -BACKTRACE_RE = r"^(0x000000008[0-9a-f]+)" - -def addr2line(): - for f in ['riscv64-unknown-elf-addr2line', 'riscv64-linux-gnu-addr2line', 'addr2line', ]: - try: - devnull = open(os.devnull) - subprocess.Popen([f], stdout=devnull, stderr=devnull).communicate() - return f - except OSError: - continue - raise AssertionError('Cannot find the addr2line program') - -@test(10, "backtrace test") -def test_backtracetest(): - r.run_qemu(shell_script([ - 'bttest' - ])) - a2l = addr2line() - matches = re.findall(BACKTRACE_RE, r.qemu.output, re.MULTILINE) - assert_equal(len(matches), 3) - files = ['sysproc.c', 'syscall.c', 'trap.c'] - for f, m in zip(files, matches): - result = subprocess.run([a2l, '-e', 'kernel/kernel', m], stdout=subprocess.PIPE) - if not f in result.stdout.decode("utf-8"): - raise AssertionError('Trace is incorrect; no %s' % f) - -@test(0, "running alarmtest") -def test_alarmtest(): - r.run_qemu(shell_script([ - 'alarmtest' - ])) - -@test(20, "alarmtest: test0", parent=test_alarmtest) -def test_alarmtest_test0(): - r.match('^test0 passed$') - -@test(20, "alarmtest: test1", parent=test_alarmtest) -def test_alarmtest_test1(): - r.match('^\\.?test1 passed$') - -@test(10, "alarmtest: test2", parent=test_alarmtest) -def test_alarmtest_test2(): - r.match('^\\.?test2 passed$') - -@test(10, "alarmtest: test3", parent=test_alarmtest) -def test_alarmtest_test3(): - r.match('^test3 passed$') - -@test(19, "usertests") -def test_usertests(): - r.run_qemu(shell_script([ - 'usertests -q' - ]), timeout=300) - r.match('^ALL TESTS PASSED$') - -@test(1, "time") -def test_time(): - check_time() - -run_tests() diff --git a/kernel/defs.h b/kernel/defs.h index 29f5a76..d1b6bb9 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -80,7 +80,6 @@ int pipewrite(struct pipe*, uint64, int); int printf(char*, ...) __attribute__ ((format (printf, 1, 2))); void panic(char*) __attribute__((noreturn)); void printfinit(void); -void backtrace(void); // proc.c int cpuid(void); diff --git a/kernel/printf.c b/kernel/printf.c index 777b0b3..d20534c 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -176,24 +176,3 @@ printfinit(void) initlock(&pr.lock, "pr"); pr.locking = 1; } - -// 打印当前调用栈的返回地址,实现简单的函数回溯,用于调试。 -void -backtrace(void) -{ - printf("barcktrace:\n"); - - uint64 ra,fp = r_fp();//frame pointer -> address - uint64 pre_fp = *((uint64*)(fp - 16)); - - // 只要当前fp和上一个fp在同一页内,就继续回溯 - while(PGROUNDDOWN(fp)==PGROUNDDOWN(pre_fp)){ - ra = *(uint64 *)(fp - 8); // 取出返回地址 - printf("%p\n", (void*)ra); // 打印返回地址 - fp = pre_fp; // 更新fp为上一个fp - pre_fp = *((uint64*)(fp - 16)); // 获取新的上一个fp - } - - ra = *(uint64 *)(fp - 8); - printf("%p\n",(void*)ra); -} \ No newline at end of file diff --git a/kernel/proc.c b/kernel/proc.c index beced5f..130d9ce 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -132,13 +132,6 @@ found: return 0; } - // 为进程分配 pre_trapframe 空间,分配失败则释放进程资源并返回 0 - if((p->pre_trapframe = (struct trapframe *)kalloc()) == 0){ - freeproc(p); - release(&p->lock); - return 0; - } - // An empty user page table. p->pagetable = proc_pagetable(p); if(p->pagetable == 0){ @@ -167,10 +160,6 @@ freeproc(struct proc *p) p->trapframe = 0; if(p->pagetable) proc_freepagetable(p->pagetable, p->sz); - // 如果进程的 pre_trapframe 存在,则释放其占用的内存,并将指针置为 0,防止悬空指针。 - if(p->pre_trapframe) - kfree((void*)p->pre_trapframe); - p->pre_trapframe = 0; p->pagetable = 0; p->sz = 0; p->pid = 0; diff --git a/kernel/proc.h b/kernel/proc.h index b3adfb7..d021857 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -104,8 +104,4 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) - int alarm_cnt; // Alarm count 触发alarm的时钟中断数量 - int inter_cnt; // interrupts count 需要统计的时钟中断的数量 - uint64 handler; // alarm func address 记录处理alarm的函数的地址 - struct trapframe *pre_trapframe; }; diff --git a/kernel/riscv.h b/kernel/riscv.h index e81715e..f7aaa8a 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -338,15 +338,6 @@ r_ra() return x; } -// 读取当前帧指针(s0)寄存器的值 -static inline uint64 -r_fp() -{ - uint64 x; - asm volatile("mv %0, s0" : "=r" (x) ); - return x; -} - // flush the TLB. static inline void sfence_vma() diff --git a/kernel/syscall.c b/kernel/syscall.c index c34b372..ed65409 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,8 +101,6 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); -extern uint64 sys_sigalarm(void); -extern uint64 sys_sigreturn(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -128,8 +126,6 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, -[SYS_sigalarm] sys_sigalarm, -[SYS_sigreturn] sys_sigreturn, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index 67ca3a4..bc5f356 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,5 +20,3 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 -#define SYS_sigalarm 22 -#define SYS_sigreturn 23 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index cd59988..3b4d5bd 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -54,8 +54,6 @@ sys_sleep(void) int n; uint ticks0; - backtrace(); - argint(0, &n); if(n < 0) n = 0; @@ -93,44 +91,3 @@ sys_uptime(void) release(&tickslock); return xticks; } - -// sys_sigalarm函数用于设置进程的定时信号处理机制。 -// 参数: -// alarm_cnt:定时器计数值,表示每经过alarm_cnt个时钟中断后触发一次信号处理。 -// addr:信号处理函数的用户空间地址。 -// 实现: -// 1. 通过argint和argaddr获取用户传入的参数。 -// 2. 将进程的inter_cnt(中断计数器)清零。 -// 3. 保存信号处理函数地址和定时器计数值到进程结构体。 -// 4. 返回0,表示设置成功。 - -// sys_sigreturn函数用于在信号处理函数执行完毕后恢复进程的上下文。 -// 实现: -// 1. 获取当前进程指针。 -// 2. 将trapframe恢复为信号处理前保存的pre_trapframe,恢复进程上下文。 -// 3. 将inter_cnt(中断计数器)清零,重新计数。 -// 4. 返回信号处理前a0寄存器的值,作为系统调用的返回值。 -uint64 -sys_sigalarm(void) -{ - int alarm_cnt; - uint64 addr; - struct proc *p = myproc(); - argint(0, &alarm_cnt); - argaddr(1, &addr); - - p->inter_cnt = 0; - p->handler = addr; - p->alarm_cnt = alarm_cnt; - - return 0; -} - -uint64 -sys_sigreturn(void) -{ - struct proc* p = myproc(); - *p->trapframe = *p->pre_trapframe; - p->inter_cnt = 0; - return p->pre_trapframe->a0; -} diff --git a/kernel/trap.c b/kernel/trap.c index 617615d..d454a7d 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -76,21 +76,9 @@ usertrap(void) if(killed(p)) exit(-1); - // 如果这是一个定时器中断,则让出CPU。 - if(which_dev == 2){ - // 当前进程的中断计数器加一。 - p->inter_cnt++; - // 如果中断计数器达到设定的报警计数,并且报警计数大于0。 - if (p->inter_cnt == p->alarm_cnt && 0 < p->alarm_cnt) { - // 备份当前trapframe到pre_trapframe,用于后续恢复。 - *p->pre_trapframe = *p->trapframe; - // 将epc设置为用户定义的handler函数地址,返回用户态时会跳转到handler执行。 - p->trapframe->epc = p->handler; - } else { - // 否则让出CPU,进行进程调度。 - yield(); - } - } + // give up the CPU if this is a timer interrupt. + if(which_dev == 2) + yield(); usertrapret(); } diff --git a/time.txt b/time.txt deleted file mode 100644 index 45a4fb7..0000000 --- a/time.txt +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/user/alarmtest.c b/user/alarmtest.c deleted file mode 100644 index b8d85f7..0000000 --- a/user/alarmtest.c +++ /dev/null @@ -1,193 +0,0 @@ -// -// test program for the alarm lab. -// you can modify this file for testing, -// but please make sure your kernel -// modifications pass the original -// versions of these tests. -// - -#include "kernel/param.h" -#include "kernel/types.h" -#include "kernel/stat.h" -#include "kernel/riscv.h" -#include "user/user.h" - -void test0(); -void test1(); -void test2(); -void test3(); -void periodic(); -void slow_handler(); -void dummy_handler(); - -int -main(int argc, char *argv[]) -{ - test0(); - test1(); - test2(); - test3(); - exit(0); -} - -volatile static int count; - -void -periodic() -{ - count = count + 1; - printf("alarm!\n"); - sigreturn(); -} - -// tests whether the kernel calls -// the alarm handler even a single time. -void -test0() -{ - int i; - printf("test0 start\n"); - count = 0; - sigalarm(2, periodic); - for(i = 0; i < 1000*500000; i++){ - if((i % 1000000) == 0) - write(2, ".", 1); - if(count > 0) - break; - } - sigalarm(0, 0); - if(count > 0){ - printf("test0 passed\n"); - } else { - printf("\ntest0 failed: the kernel never called the alarm handler\n"); - } -} - -void __attribute__ ((noinline)) foo(int i, int *j) { - if((i % 2500000) == 0) { - write(2, ".", 1); - } - *j += 1; -} - -// -// tests that the kernel calls the handler multiple times. -// -// tests that, when the handler returns, it returns to -// the point in the program where the timer interrupt -// occurred, with all registers holding the same values they -// held when the interrupt occurred. -// -void -test1() -{ - int i; - int j; - - printf("test1 start\n"); - count = 0; - j = 0; - sigalarm(2, periodic); - for(i = 0; i < 500000000; i++){ - if(count >= 10) - break; - foo(i, &j); - } - if(count < 10){ - printf("\ntest1 failed: too few calls to the handler\n"); - } else if(i != j){ - // the loop should have called foo() i times, and foo() should - // have incremented j once per call, so j should equal i. - // once possible source of errors is that the handler may - // return somewhere other than where the timer interrupt - // occurred; another is that that registers may not be - // restored correctly, causing i or j or the address ofj - // to get an incorrect value. - printf("\ntest1 failed: foo() executed fewer times than it was called\n"); - } else { - printf("test1 passed\n"); - } -} - -// -// tests that kernel does not allow reentrant alarm calls. -void -test2() -{ - int i; - int pid; - int status; - - printf("test2 start\n"); - if ((pid = fork()) < 0) { - printf("test2: fork failed\n"); - } - if (pid == 0) { - count = 0; - sigalarm(2, slow_handler); - for(i = 0; i < 1000*500000; i++){ - if((i % 1000000) == 0) - write(2, ".", 1); - if(count > 0) - break; - } - if (count == 0) { - printf("\ntest2 failed: alarm not called\n"); - exit(1); - } - exit(0); - } - wait(&status); - if (status == 0) { - printf("test2 passed\n"); - } -} - -void -slow_handler() -{ - count++; - printf("alarm!\n"); - if (count > 1) { - printf("test2 failed: alarm handler called more than once\n"); - exit(1); - } - for (int i = 0; i < 1000*500000; i++) { - asm volatile("nop"); // avoid compiler optimizing away loop - } - sigalarm(0, 0); - sigreturn(); -} - -// -// dummy alarm handler; after running immediately uninstall -// itself and finish signal handling -void -dummy_handler() -{ - sigalarm(0, 0); - sigreturn(); -} - -// -// tests that the return from sys_sigreturn() does not -// modify the a0 register -void -test3() -{ - uint64 a0; - - sigalarm(1, dummy_handler); - printf("test3 start\n"); - - asm volatile("lui a5, 0"); - asm volatile("addi a0, a5, 0xac" : : : "a0"); - for(int i = 0; i < 500000000; i++) - ; - asm volatile("mv %0, a0" : "=r" (a0) ); - - if(a0 != 0xac) - printf("test3 failed: register a0 changed\n"); - else - printf("test3 passed\n"); -} diff --git a/user/bttest.c b/user/bttest.c deleted file mode 100644 index 05405f9..0000000 --- a/user/bttest.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "kernel/types.h" -#include "kernel/stat.h" -#include "user/user.h" - -int -main(int argc, char *argv[]) -{ - sleep(1); - exit(0); -} diff --git a/user/call.c b/user/call.c deleted file mode 100644 index f725dcb..0000000 --- a/user/call.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "kernel/param.h" -#include "kernel/types.h" -#include "kernel/stat.h" -#include "user/user.h" - -int g(int x) { - return x+3; -} - -int f(int x) { - return g(x); -} - -void main(void) { - printf("%d %d\n", f(8)+1, 13); - exit(0); -} diff --git a/user/cowtest.c b/user/cowtest.c new file mode 100644 index 0000000..1de14ed --- /dev/null +++ b/user/cowtest.c @@ -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); +} diff --git a/user/user.h b/user/user.h index 81a2910..f16fe27 100644 --- a/user/user.h +++ b/user/user.h @@ -22,8 +22,6 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); -int sigalarm(int, void(*)()); -int sigreturn(void); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usertests.c b/user/usertests.c index 4e04881..28b53f9 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -244,7 +244,7 @@ copyinstr3(char *s) // See if the kernel refuses to read/write user memory that the // application doesn't have anymore, because it returned it. void -rwsbrk(char* argv) +rwsbrk() { int fd, n; diff --git a/user/usys.pl b/user/usys.pl index fa548b0..01e426e 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,5 +36,3 @@ entry("getpid"); entry("sbrk"); entry("sleep"); entry("uptime"); -entry("sigalarm"); -entry("sigreturn");