modify the 05-Preemptive.md

This commit is contained in:
ianchen0119
2021-06-13 12:38:59 +08:00
parent 28dd4d6f1a
commit a108224617
4 changed files with 134 additions and 71 deletions

View File

@@ -138,12 +138,12 @@ trap_vector:
# trap_handler will return the return address via a0.
csrw mepc, a0
# restore context(registers).
# load context(registers).
csrr t6, mscratch
reg_load t6
la a1, os_kernel # mepc = sys_kernel
csrw mepc, a1 # mret : will jump to sys_kernel
# return to whatever we were doing before trap.
# jump to sys_kernel
la a1, os_kernel # a1 = os_kernel
csrw mepc, a1 # mepc = sys_kernel
mret

View File

@@ -1,7 +1,5 @@
#include "timer.h"
extern void os_kernel();
// a scratch area per CPU for machine-mode timer interrupts.
reg_t timer_scratch[NCPU][5];

View File

@@ -1,5 +1,8 @@
mini-riscv-os is written by:
Chung-Chen Chen <ccckmit@gmail.com>
Other contributors:
Ian Chen <ychen.desl@gmail.com>
Copyrighted by:
National Quemoy University, Taiwan

View File

@@ -1,16 +1,11 @@
# 05-Preemptive -- RISC-V 的嵌入式作業系統
[lib.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/lib.c
[os.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
[timer.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/timer.c
[sys.s]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/sys.s
[task.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/task.c
[user.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/user.c
[lib.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/lib.c
[os.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
[timer.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/timer.c
[sys.s]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/sys.s
[task.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/task.c
[user.c]: https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/user.c
專案 -- https://github.com/ccc-c/mini-riscv-os/tree/master/05-Preemptive
@@ -115,7 +110,7 @@ void lib_delay(volatile int count)
## 作業系統 [os.c]
* https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
- https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/os.c
作業系統 os.c 一開始會呼叫 user_init() ,讓使用者建立 task (在本範例中會在 [user.c] 裏建立 user_task0 與 user_task1。
@@ -178,16 +173,102 @@ int os_main(void)
}
```
05-Preemptive 的時間中斷原理和上一章相同,都是在 [timer.c] 的 timer_init() 中設定好第一次的時間中斷。
05-Preemptive 的中斷機制中,我們修改了中斷向量表:
```c=
.globl trap_vector
# the trap vector base address must always be aligned on a 4-byte boundary
.align 4
trap_vector:
# save context(registers).
csrrw t6, mscratch, t6 # swap t6 and mscratch
reg_save t6
csrw mscratch, t6
# call the C trap handler in trap.c
csrr a0, mepc
csrr a1, mcause
call trap_handler
# trap_handler will return the return address via a0.
csrw mepc, a0
# load context(registers).
csrr t6, mscratch
reg_load t6
# jump to sys_kernel
la a1, os_kernel # a1 = os_kernel
csrw mepc, a1 # mepc = sys_kernel
mret
```
當中斷發生時,中斷向量表 `trap_vector()` 會呼叫 `trap_handler()` :
```c=
reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & 0xfff;
if (cause & 0x80000000)
{
/* Asynchronous trap - interrupt */
switch (cause_code)
{
case 3:
lib_puts("software interruption!\n");
break;
case 7:
lib_puts("timer interruption!\n");
timer_handler();
break;
case 11:
lib_puts("external interruption!\n");
break;
default:
lib_puts("unknown async exception!\n");
break;
}
}
else
{
/* Synchronous trap - exception */
lib_puts("Sync exceptions!\n");
while (1)
{
/* code */
}
}
return return_pc;
}
```
跳到 `trap_handler()` 之後,它會針對不同類型的中斷呼叫不同的 handler ,所以我們可以將它視為一個中斷的派發任務中繼站:
```
+----------------+
| soft_handler() |
+-------+----------------+
|
+----------------+-------+-----------------+
| trap_handler() | | timer_handler() |
+----------------+ +-----------------+
|
+-------+-----------------+
| exter_handler() |
+-----------------+
```
`trap_handler` 可以根據不同的中斷類型,將中斷處理交給不同的 handler ,這樣子做就可以大大的提高作業系統的擴充性。
```cpp
#include "timer.h"
extern void os_kernel();
// a scratch area per CPU for machine-mode timer interrupts.
reg_t timer_scratch[NCPU][5];
#define interval 20000000 // cycles; about 2 second in qemu.
void timer_init()
{
// each CPU has a separate source of timer interrupts.
@@ -195,8 +276,8 @@ void timer_init()
// ask the CLINT for a timer interrupt.
// int interval = 1000000; // cycles; about 1/10th second in qemu.
int interval = 20000000; // cycles; about 2 second in qemu.
*(reg_t*)CLINT_MTIMECMP(id) = *(reg_t*)CLINT_MTIME + interval;
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
// prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers.
@@ -207,48 +288,33 @@ void timer_init()
scratch[4] = interval;
w_mscratch((reg_t)scratch);
// set the machine-mode trap handler.
w_mtvec((reg_t)sys_timer);
// enable machine-mode interrupts.
w_mstatus(r_mstatus() | MSTATUS_MIE);
// enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE);
}
static int timer_count = 0;
void timer_handler() {
void timer_handler()
{
lib_printf("timer_handler: %d\n", ++timer_count);
os_kernel();
int id = r_mhartid();
*(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval;
}
```
但不同的是,[sys.s] 裏的 sys_timer 會呼叫上面 [timer.c] 裏的 timer_handler(),其中包含了 `os_kernel()` 這個函數,該函數會呼叫 [task.c] 裏的 task_os() task_os() 會呼叫 [sys.s] 裏的 sys_switch 去切換回 kernel於是作業系統就透過時間中斷將控制權強制取回來了
看到 [timer.c] 裏的 `timer_handler()`,它會將 `MTIMECMP` 做 reset 的動作,等到 `timer_handler()` 執行完畢,中斷向量表 `trap_vector()` 會將 mepc 指向 `os_kernel()` ,做到任務切換的功能
```s
# ...
sys_switch:
ctx_save a0 # a0 => struct context *old
ctx_load a1 # a1 => struct context *new
ret # pc=ra; swtch to new task (new->ra)
# ...
sys_kernel:
addi sp, sp, -128 # alloc stack space
reg_save sp # save all registers
call timer_handler # call timer_handler in timer.c
reg_load sp # restore all registers
addi sp, sp, 128 # restore stack pointer
jr a7 # jump to a7=mepc , return to timer break point
# ...
sys_timer:
# ...
csrr a7, mepc # a7 = mepc, for sys_kernel jump back to interrupted point
la a1, sys_kernel # mepc = sys_kernel
csrw mepc, a1 # mret : will jump to sys_kernel
# ...
最後,記得在 Kernel 開機時導入 trap 以及 timer 的初始化動作:
```c=
void os_start()
{
lib_puts("OS start\n");
user_init();
trap_init();
timer_init(); // start timer interrupt ...
}
```
透過時間中斷強制取回控制權,我們就不用擔心有惡霸行程把持 CPU 不放,系統也就不會被惡霸卡住而整個癱瘓了,這就是現代作業系統中最重要的《行程管理機制》。
@@ -259,29 +325,25 @@ sys_timer:
還好,這些事情已經有人做好了,您可以透過學習 xv6-riscv 這個由 MIT 所設計的教學型作業系統進一步了解這些較複雜的機制xv6-riscv 的原始碼總共有八千多行,雖然不算太少,但是比起那些動則數百萬行到數千萬行的 Linux / Windows 而言xv6-riscv 算是非常精簡的系統了。
* https://github.com/mit-pdos/xv6-riscv
- https://github.com/mit-pdos/xv6-riscv
然而 xv6-riscv 原本只能在 linux 下編譯執行,但是我把其中的 mkfs/mkfs.c 修改了一下,就能在 windows + git bash 這樣和 mini-riscv-os 一樣的環境下編譯執行了。
您可以從下列網址中取得 windows 版的 xv6-riscv 原始碼,然後編譯執行看看,應該可以站在 mini-riscv-os 的基礎上,進一步透過 xv6-riscv 學習更進階的作業系統設計原理。
* https://github.com/ccc-c/xv6-riscv-win
- https://github.com/ccc-c/xv6-riscv-win
以下提供更多關於 RISC-V 的學習資源,以方便大家在學習 RISC-V 作業系統設計時,不需再經過太多的摸索。
* [RISC-V 手册 - 一本开源指令集的指南 (PDF)](http://crva.ict.ac.cn/documents/RISC-V-Reader-Chinese-v2p1.pdf)
* [The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture (PDF)](https://riscv.org//wp-content/uploads/2019/12/riscv-spec-20191213.pdf)
* [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md)
* https://github.com/riscv/riscv-opcodes
* https://github.com/riscv/riscv-opcodes/blob/master/opcodes-rv32i
* [SiFive Interrupt Cookbook (SiFive 的 RISC-V 中斷手冊)](https://gitlab.com/ccc109/sp/-/blob/master/10-riscv/mybook/riscv-interrupt/sifive-interrupt-cookbook-zh.md)
* [SiFive Interrupt Cookbook -- Version 1.0 (PDF)](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf)
* 進階: [proposal for a RISC-V Core-Local Interrupt Controller (CLIC)](https://github.com/riscv/riscv-fast-interrupt/blob/master/clic.adoc)
- [RISC-V 手册 - 一本开源指令集的指南 (PDF)](http://crva.ict.ac.cn/documents/RISC-V-Reader-Chinese-v2p1.pdf)
- [The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture (PDF)](https://riscv.org//wp-content/uploads/2019/12/riscv-spec-20191213.pdf)
- [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md)
- https://github.com/riscv/riscv-opcodes
- https://github.com/riscv/riscv-opcodes/blob/master/opcodes-rv32i
- [SiFive Interrupt Cookbook (SiFive 的 RISC-V 中斷手冊)](https://gitlab.com/ccc109/sp/-/blob/master/10-riscv/mybook/riscv-interrupt/sifive-interrupt-cookbook-zh.md)
- [SiFive Interrupt Cookbook -- Version 1.0 (PDF)](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf)
- 進階: [proposal for a RISC-V Core-Local Interrupt Controller (CLIC)](https://github.com/riscv/riscv-fast-interrupt/blob/master/clic.adoc)
希望這份 mini-riscv-os 教材能幫助讀者在學習 RISC-V OS 設計上節省一些寶貴的時間!
陳鍾誠 2020/11/15 於金門大學