diff --git a/doc/memory.md b/doc/内核原理-内存管理.md similarity index 100% rename from doc/memory.md rename to doc/内核原理-内存管理.md diff --git a/doc/内核原理-系统调用.md b/doc/内核原理-系统调用.md new file mode 100644 index 0000000..4ce99e0 --- /dev/null +++ b/doc/内核原理-系统调用.md @@ -0,0 +1,39 @@ +# System Call +System Call即系统调用(以下简称syscall),用于在用户程序中执行一些特权模式下才能完成的操作(如I/O操作)。 +对于处理器而言,syscall是一种同步发生的事件,在基于RISC-V的操作系统中由`ecall`指令(Environment Call)产生。 +当系统调用事件结束后,处理器将返回到ecall指令的下一条指令继续执行。 + +原版xv6的文档将“system call”、“exception”和“(device) interrupt”统称为“trap”。 +在RISC-V指令集手册的1.6节中,对“exception”、“interrupt”和“trap”的定义是这样的: + +> We use the term *exception* to refer to an unusual condition occurring at run time associated with +an instruction in the current RISC-V hart. We use the term *interrupt* to refer to an external +asynchronous event that may cause a RISC-V hart to experience an unexpected transfer of control. +We use the term *trap* to refer to the transfer of control to a trap handler caused by either an +exception or an interrupt. + +从这个角度来看,trap在RISC-V的语境下指的是控制权的转换,也就是特权级的转换。这可能与我们平常使用的术语有一定出入,需要注意。 + +## 在RISC-V中请求系统调用 +在RISC-V的用户程序中,使用`ecall`指令请求syscall。操作系统提供不同功能的syscall,对应不同的请求调用号。 +因此请求前,需要向`a7`寄存器中写入所请求的syscall的调用号。以下为一个请求8号调用的例子: + +``` +li a7, 8 +ecall +``` + +## 系统调用的处理过程 +Syscall涉及到特权态的转换,需要经过trap过程。对于用户程序而言,一般是从用户模式(U模式)陷入到监管模式(S模式)。 +我们知道,操作系统中有中断向量表,保存着处理各类中断的中断例程的入口,syscall的中断处理例程自然也在其中。 +而对于不同类型的调用请求,syscall的处理例程也有相应的系统调用表,保存不同类型的系统调用功能函数的入口。 +根据用户事先写入寄存器的系统调用号,syscall的处理例程调用相应的功能函数,完成系统调用,随后返回至原先的特权态。 + +
+
+ +# 参考文档 + ++ [*xv6: a simple, Unix-like teaching operating system*](https://pdos.csail.mit.edu/6.S081/2020/xv6/book-riscv-rev1.pdf) ++ [*The RISC-V Instruction Set Manual Volume I: Unprivileged ISA*](https://github.com/riscv/riscv-isa-manual/releases/download/draft-20210212-c879d5a/riscv-spec.pdf) ++ [《RISC-V手册——一本开源指令集的指南》](http://riscvbook.com/chinese/RISC-V-Reader-Chinese-v2p1.pdf) \ No newline at end of file diff --git a/doc/内核原理-进程管理.md b/doc/内核原理-进程管理.md new file mode 100644 index 0000000..225936d --- /dev/null +++ b/doc/内核原理-进程管理.md @@ -0,0 +1,8 @@ +# 进程管理 + +## 自旋锁 +讨论进程管理,就必须先搞懂自旋锁。 + +1. 为了避免死锁,加锁和释放锁中涉及开关中断,为了保证释放锁之后与加锁之前的中断使能标志保持一致,需要保存加锁之前关中断时的中断使能状态,在释放锁之后开中断时将中断使能恢复到之前所保存的状态。(即封装之后的push_off()和pop_off()) +2. 由于现代编译器的代码优化和指令重排以及现代CPU的乱序执行(Out-of-Order Execution),在上锁期间需要额外的操作来保证读写操作的顺序一致性,在RISC-V中可以用__sync_synchronize指令来保证严格的执行顺序。(注:关于这个问题可以具体参考[这篇文章](https://www.0xffffff.org/2017/02/21/40-atomic-variable-mutex-and-memory-barrier/)) + diff --git a/doc/sdcard.md b/doc/构建调试-SD卡驱动.md similarity index 100% rename from doc/sdcard.md rename to doc/构建调试-SD卡驱动.md diff --git a/doc/s_extern_interrupt.md b/doc/构建调试-S态外部中断.md similarity index 100% rename from doc/s_extern_interrupt.md rename to doc/构建调试-S态外部中断.md diff --git a/doc/boot.md b/doc/构建调试-开机启动.md similarity index 100% rename from doc/boot.md rename to doc/构建调试-开机启动.md diff --git a/doc/fs.md b/doc/构建调试-文件系统.md similarity index 100% rename from doc/fs.md rename to doc/构建调试-文件系统.md diff --git a/doc/timer.md b/doc/构建调试-时钟中断.md similarity index 100% rename from doc/timer.md rename to doc/构建调试-时钟中断.md diff --git a/doc/syscall.md b/doc/构建调试-系统调用.md similarity index 58% rename from doc/syscall.md rename to doc/构建调试-系统调用.md index e2a9dd7..ff81e47 100644 --- a/doc/syscall.md +++ b/doc/构建调试-系统调用.md @@ -1,43 +1,3 @@ -+ [System Call的执行](#Syscall在xv6-k210中的实现与执行) -+ [System Call的添加](#如何在xv6-k210中添加syscall) - -
-
- -# System Call -System Call即系统调用(以下简称syscall),用于在用户程序中执行一些特权模式下才能完成的操作(如I/O操作)。 -对于处理器而言,syscall是一种同步发生的事件,在基于RISC-V的操作系统中由`ecall`指令(Environment Call)产生。 -当系统调用事件结束后,处理器将返回到ecall指令的下一条指令继续执行。 - -原版xv6的文档将“system call”、“exception”和“(device) interrupt”统称为“trap”。 -在RISC-V指令集手册的1.6节中,对“exception”、“interrupt”和“trap”的定义是这样的: - -> We use the term *exception* to refer to an unusual condition occurring at run time associated with -an instruction in the current RISC-V hart. We use the term *interrupt* to refer to an external -asynchronous event that may cause a RISC-V hart to experience an unexpected transfer of control. -We use the term *trap* to refer to the transfer of control to a trap handler caused by either an -exception or an interrupt. - -从这个角度来看,trap在RISC-V的语境下指的是控制权的转换,也就是特权级的转换。这可能与我们平常使用的术语有一定出入,需要注意。 - -## 在RISC-V中请求系统调用 -在RISC-V的用户程序中,使用`ecall`指令请求syscall。操作系统提供不同功能的syscall,对应不同的请求调用号。 -因此请求前,需要向`a7`寄存器中写入所请求的syscall的调用号。以下为一个请求8号调用的例子: - -``` -li a7, 8 -ecall -``` - -## 系统调用的处理过程 -Syscall涉及到特权态的转换,需要经过trap过程。对于用户程序而言,一般是从用户模式(U模式)陷入到监管模式(S模式)。 -我们知道,操作系统中有中断向量表,保存着处理各类中断的中断例程的入口,syscall的中断处理例程自然也在其中。 -而对于不同类型的调用请求,syscall的处理例程也有相应的系统调用表,保存不同类型的系统调用功能函数的入口。 -根据用户事先写入寄存器的系统调用号,syscall的处理例程调用相应的功能函数,完成系统调用,随后返回至原先的特权态。 - -
-
- # Syscall在xv6-k210中的实现与执行 Syscall是如何实现与执行的呢?以下从用户程序出发,分析在xv6-k210中系统调用的过程。 @@ -202,101 +162,3 @@ static uint64 (*syscalls[])(void) = {

- -# 如何在xv6-k210中添加syscall - -了解了Syscall在xv6-k210中的执行过程,为系统添加一个新的syscall功能就不是什么难事了。添加新系统调用的步骤如下: - -1. 在xv6-uesr目录下 - + 在uesr.h文件中,添加新系统调用封装后的函数声明,假设其函数名为`mysyscall`。 - + 在usys.pl文件末尾,添加如下行: - - ```perl - ...... - entry("mysyscall"); - ``` - -2. 在kernel目录下 - + 在include/syscall.h文件中,添加新系统调用号的宏定义: - - ```C - ...... - #define SYS_mysyscall ? - ``` - - 其中,“?”为新的合法系统调用号,本质上是一个数组下标,可根据需要设置,建议按顺序递增添加。 - - + 在syscall.c文件中,添加功能函数的声明,并更新系统调用表: - - ```C - ...... - extern uint64 sys_mysyscall(void); - - static uint64 (*syscalls[])(void) = { - ...... - [SYS_mysyscall] sys_mysyscall, - }; - - ``` -3. 根据该系统调用的功能,选择一个适合的内核模块的源文件,在其中实现`sys_mysyscall`函数的功能。 - 例如,与文件相关的系统调用可以在kernel/sysfile.c中添加。 - 可以利用syscall.c文件中提供的相关函数,在`sys_mysyscall`中获取用户进程传递的参数。 - -下面给出一个更具体的例子。在我们的移植工作中,我们需要测试用户进程的运行,使其向屏幕进行输出。 -但当时用户态尚未支持`printf`函数,我们便通过系统调用,在S态进行一些输出。为此,我们需要添加一个`test_proc`函数。 - -首先,我们在xv6-uesr/uesr.h文件中添加函数声明,为用户程序添加调用接口: -```C -int test_proc(void); -``` -在xv6-uesr/usys.pl中添加行: -```perl -entry("test_proc"); -``` -随后修改内核,在kernel/include/syscall.h中添加调用号,调用号可以自行选择,这里我们选择22,表示第22个系统调用。 -```C -#define SYS_test_proc 22 -``` -最后,在kernel/include/syscall.c中修改系统调用表,以及添加函数`sys_test_proc`: -```C -extern uint64 sys_test_proc(void); - -static uint64 (*syscalls[])(void) = { - ...... - [SYS_test_proc] sys_test_proc, -}; - -uint64 sys_test_proc(void) { - printf("hello world from proc %d, hart %d\n", myproc()->pid, r_tp()); - return 0; -} -``` -至此,我们就已经成功添加了一个系统调用。在用户程序中可以使用这个系统调用,例如以下的用户程序实例: -```C -#include "kernel/include/types.h" -#include "kernel/include/stat.h" -#include "xv6-user/user.h" - -int main() -{ - int n = 10; - while (n--) { - test_proc(); - } - exit(0); -} -``` -编译运行后的结果如下图所示(编译用户程序的方法详见[此处](./fs.md)): - -![](/img/syscall_test.png) - - -
-
- -# 参考文档 - -+ [*xv6: a simple, Unix-like teaching operating system*](https://pdos.csail.mit.edu/6.S081/2020/xv6/book-riscv-rev1.pdf) -+ [*The RISC-V Instruction Set Manual Volume I: Unprivileged ISA*](https://github.com/riscv/riscv-isa-manual/releases/download/draft-20210212-c879d5a/riscv-spec.pdf) -+ [《RISC-V手册——一本开源指令集的指南》](http://riscvbook.com/chinese/RISC-V-Reader-Chinese-v2p1.pdf) - diff --git a/doc/proc.md b/doc/构建调试-进程管理.md similarity index 100% rename from doc/proc.md rename to doc/构建调试-进程管理.md diff --git a/doc/memory-code.md b/doc/用户使用-内存管理.md similarity index 100% rename from doc/memory-code.md rename to doc/用户使用-内存管理.md diff --git a/doc/用户使用-系统调用.md b/doc/用户使用-系统调用.md new file mode 100644 index 0000000..0307e0c --- /dev/null +++ b/doc/用户使用-系统调用.md @@ -0,0 +1,90 @@ +# 如何在xv6-k210中添加syscall + +了解了Syscall在xv6-k210中的执行过程,为系统添加一个新的syscall功能就不是什么难事了。添加新系统调用的步骤如下: + +1. 在xv6-uesr目录下 + + 在uesr.h文件中,添加新系统调用封装后的函数声明,假设其函数名为`mysyscall`。 + + 在usys.pl文件末尾,添加如下行: + + ```perl + ...... + entry("mysyscall"); + ``` + +2. 在kernel目录下 + + 在include/syscall.h文件中,添加新系统调用号的宏定义: + + ```C + ...... + #define SYS_mysyscall ? + ``` + + 其中,“?”为新的合法系统调用号,本质上是一个数组下标,可根据需要设置,建议按顺序递增添加。 + + + 在syscall.c文件中,添加功能函数的声明,并更新系统调用表: + + ```C + ...... + extern uint64 sys_mysyscall(void); + + static uint64 (*syscalls[])(void) = { + ...... + [SYS_mysyscall] sys_mysyscall, + }; + + ``` +3. 根据该系统调用的功能,选择一个适合的内核模块的源文件,在其中实现`sys_mysyscall`函数的功能。 + 例如,与文件相关的系统调用可以在kernel/sysfile.c中添加。 + 可以利用syscall.c文件中提供的相关函数,在`sys_mysyscall`中获取用户进程传递的参数。 + +下面给出一个更具体的例子。在我们的移植工作中,我们需要测试用户进程的运行,使其向屏幕进行输出。 +但当时用户态尚未支持`printf`函数,我们便通过系统调用,在S态进行一些输出。为此,我们需要添加一个`test_proc`函数。 + +首先,我们在xv6-uesr/uesr.h文件中添加函数声明,为用户程序添加调用接口: +```C +int test_proc(void); +``` +在xv6-uesr/usys.pl中添加行: +```perl +entry("test_proc"); +``` +随后修改内核,在kernel/include/syscall.h中添加调用号,调用号可以自行选择,这里我们选择22,表示第22个系统调用。 +```C +#define SYS_test_proc 22 +``` +最后,在kernel/include/syscall.c中修改系统调用表,以及添加函数`sys_test_proc`: +```C +extern uint64 sys_test_proc(void); + +static uint64 (*syscalls[])(void) = { + ...... + [SYS_test_proc] sys_test_proc, +}; + +uint64 sys_test_proc(void) { + printf("hello world from proc %d, hart %d\n", myproc()->pid, r_tp()); + return 0; +} +``` +至此,我们就已经成功添加了一个系统调用。在用户程序中可以使用这个系统调用,例如以下的用户程序实例: +```C +#include "kernel/include/types.h" +#include "kernel/include/stat.h" +#include "xv6-user/user.h" + +int main() +{ + int n = 10; + while (n--) { + test_proc(); + } + exit(0); +} +``` +编译运行后的结果如下图所示(编译用户程序的方法详见[此处](./fs.md)): + +![](/img/syscall_test.png) + + +
+
diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 1c9e958..5d2c1c2 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -13,7 +13,6 @@ void freerange(void *pa_start, void *pa_end); extern char kernel_end[]; // first address after kernel. - // defined by k210.ld. struct run { struct run *next;