mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 04:24:33 +00:00
modify the 05-Preemptive.md
This commit is contained in:
@@ -138,12 +138,12 @@ trap_vector:
|
|||||||
# trap_handler will return the return address via a0.
|
# trap_handler will return the return address via a0.
|
||||||
csrw mepc, a0
|
csrw mepc, a0
|
||||||
|
|
||||||
# restore context(registers).
|
# load context(registers).
|
||||||
csrr t6, mscratch
|
csrr t6, mscratch
|
||||||
reg_load t6
|
reg_load t6
|
||||||
|
|
||||||
la a1, os_kernel # mepc = sys_kernel
|
# jump to sys_kernel
|
||||||
csrw mepc, a1 # mret : will jump to sys_kernel
|
la a1, os_kernel # a1 = os_kernel
|
||||||
# return to whatever we were doing before trap.
|
csrw mepc, a1 # mepc = sys_kernel
|
||||||
mret
|
mret
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
extern void os_kernel();
|
|
||||||
|
|
||||||
// a scratch area per CPU for machine-mode timer interrupts.
|
// a scratch area per CPU for machine-mode timer interrupts.
|
||||||
reg_t timer_scratch[NCPU][5];
|
reg_t timer_scratch[NCPU][5];
|
||||||
|
|
||||||
|
|||||||
3
AUTHORS
3
AUTHORS
@@ -1,5 +1,8 @@
|
|||||||
mini-riscv-os is written by:
|
mini-riscv-os is written by:
|
||||||
Chung-Chen Chen <ccckmit@gmail.com>
|
Chung-Chen Chen <ccckmit@gmail.com>
|
||||||
|
|
||||||
|
Other contributors:
|
||||||
|
Ian Chen <ychen.desl@gmail.com>
|
||||||
|
|
||||||
Copyrighted by:
|
Copyrighted by:
|
||||||
National Quemoy University, Taiwan
|
National Quemoy University, Taiwan
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
# 05-Preemptive -- RISC-V 的嵌入式作業系統
|
# 05-Preemptive -- RISC-V 的嵌入式作業系統
|
||||||
|
|
||||||
[lib.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/lib.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
|
||||||
[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
|
||||||
[timer.c]:https://github.com/ccc-c/mini-riscv-os/blob/master/05-Preemptive/timer.c
|
[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
|
||||||
[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
|
專案 -- https://github.com/ccc-c/mini-riscv-os/tree/master/05-Preemptive
|
||||||
|
|
||||||
第三章的 [03-MultiTasking](03-MultiTasking.md) 中我們實作了一個《協同式多工》作業系統。不過由於沒有引入時間中斷機制,無法成為一個《搶先式》(Preemptive) 多工系統。
|
第三章的 [03-MultiTasking](03-MultiTasking.md) 中我們實作了一個《協同式多工》作業系統。不過由於沒有引入時間中斷機制,無法成為一個《搶先式》(Preemptive) 多工系統。
|
||||||
|
|
||||||
第四章的 [04-TimerInterrupt](04-TimerInterrupt.md) 中我們示範了 RISC-V 的時間中斷機制原理。
|
第四章的 [04-TimerInterrupt](04-TimerInterrupt.md) 中我們示範了 RISC-V 的時間中斷機制原理。
|
||||||
|
|
||||||
終於到了第五章,我們打算結合前兩章的技術,實作一個具有強制時間中斷的《可搶先式》(Preemptive) 作業系統。這樣的系統就可以算是一個微型的嵌入式作業系統了。
|
終於到了第五章,我們打算結合前兩章的技術,實作一個具有強制時間中斷的《可搶先式》(Preemptive) 作業系統。這樣的系統就可以算是一個微型的嵌入式作業系統了。
|
||||||
|
|
||||||
@@ -24,7 +19,7 @@
|
|||||||
|
|
||||||
首先讓我們和看系統的執行狀況,您可以看到下列執行結果中,系統在 OS, Task0, Task1 之間輪流的切換著。
|
首先讓我們和看系統的執行狀況,您可以看到下列執行結果中,系統在 OS, Task0, Task1 之間輪流的切換著。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make qemu
|
$ make qemu
|
||||||
Press Ctrl-A and then X to exit QEMU
|
Press Ctrl-A and then X to exit QEMU
|
||||||
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf
|
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf
|
||||||
@@ -115,7 +110,7 @@ void lib_delay(volatile int count)
|
|||||||
|
|
||||||
## 作業系統 [os.c]
|
## 作業系統 [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。
|
作業系統 os.c 一開始會呼叫 user_init() ,讓使用者建立 task (在本範例中會在 [user.c] 裏建立 user_task0 與 user_task1。
|
||||||
|
|
||||||
@@ -149,7 +144,7 @@ void user_init() {
|
|||||||
然後作業系統會在 os_start() 裏透過 timer_init() 函數設定時間中斷,接著就是進入 os_main() 的主迴圈裏,該迴圈採用 Round-Robin 的大輪迴排班方法,每次切換就選下一個 task 來執行 (若已到最後一個 task ,接下來就是第 0 個 task)。
|
然後作業系統會在 os_start() 裏透過 timer_init() 函數設定時間中斷,接著就是進入 os_main() 的主迴圈裏,該迴圈採用 Round-Robin 的大輪迴排班方法,每次切換就選下一個 task 來執行 (若已到最後一個 task ,接下來就是第 0 個 task)。
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
void os_kernel() {
|
void os_kernel() {
|
||||||
@@ -165,7 +160,7 @@ void os_start() {
|
|||||||
int os_main(void)
|
int os_main(void)
|
||||||
{
|
{
|
||||||
os_start();
|
os_start();
|
||||||
|
|
||||||
int current_task = 0;
|
int current_task = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
lib_puts("OS: Activate next task\n");
|
lib_puts("OS: Activate next task\n");
|
||||||
@@ -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
|
```cpp
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
extern void os_kernel();
|
|
||||||
|
|
||||||
// a scratch area per CPU for machine-mode timer interrupts.
|
// a scratch area per CPU for machine-mode timer interrupts.
|
||||||
reg_t timer_scratch[NCPU][5];
|
reg_t timer_scratch[NCPU][5];
|
||||||
|
|
||||||
|
#define interval 20000000 // cycles; about 2 second in qemu.
|
||||||
|
|
||||||
void timer_init()
|
void timer_init()
|
||||||
{
|
{
|
||||||
// each CPU has a separate source of timer interrupts.
|
// each CPU has a separate source of timer interrupts.
|
||||||
@@ -195,8 +276,8 @@ void timer_init()
|
|||||||
|
|
||||||
// ask the CLINT for a timer interrupt.
|
// ask the CLINT for a timer interrupt.
|
||||||
// int interval = 1000000; // cycles; about 1/10th second in qemu.
|
// 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.
|
// prepare information in scratch[] for timervec.
|
||||||
// scratch[0..2] : space for timervec to save registers.
|
// scratch[0..2] : space for timervec to save registers.
|
||||||
@@ -207,48 +288,33 @@ void timer_init()
|
|||||||
scratch[4] = interval;
|
scratch[4] = interval;
|
||||||
w_mscratch((reg_t)scratch);
|
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.
|
// enable machine-mode timer interrupts.
|
||||||
w_mie(r_mie() | MIE_MTIE);
|
w_mie(r_mie() | MIE_MTIE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer_count = 0;
|
static int timer_count = 0;
|
||||||
|
|
||||||
void timer_handler() {
|
void timer_handler()
|
||||||
|
{
|
||||||
lib_printf("timer_handler: %d\n", ++timer_count);
|
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
|
最後,記得在 Kernel 開機時導入 trap 以及 timer 的初始化動作:
|
||||||
# ...
|
|
||||||
sys_switch:
|
```c=
|
||||||
ctx_save a0 # a0 => struct context *old
|
void os_start()
|
||||||
ctx_load a1 # a1 => struct context *new
|
{
|
||||||
ret # pc=ra; swtch to new task (new->ra)
|
lib_puts("OS start\n");
|
||||||
# ...
|
user_init();
|
||||||
sys_kernel:
|
trap_init();
|
||||||
addi sp, sp, -128 # alloc stack space
|
timer_init(); // start timer interrupt ...
|
||||||
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
|
|
||||||
# ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
透過時間中斷強制取回控制權,我們就不用擔心有惡霸行程把持 CPU 不放,系統也就不會被惡霸卡住而整個癱瘓了,這就是現代作業系統中最重要的《行程管理機制》。
|
透過時間中斷強制取回控制權,我們就不用擔心有惡霸行程把持 CPU 不放,系統也就不會被惡霸卡住而整個癱瘓了,這就是現代作業系統中最重要的《行程管理機制》。
|
||||||
@@ -259,29 +325,25 @@ sys_timer:
|
|||||||
|
|
||||||
還好,這些事情已經有人做好了,您可以透過學習 xv6-riscv 這個由 MIT 所設計的教學型作業系統,進一步了解這些較複雜的機制,xv6-riscv 的原始碼總共有八千多行,雖然不算太少,但是比起那些動則數百萬行到數千萬行的 Linux / Windows 而言,xv6-riscv 算是非常精簡的系統了。
|
還好,這些事情已經有人做好了,您可以透過學習 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 一樣的環境下編譯執行了。
|
然而 xv6-riscv 原本只能在 linux 下編譯執行,但是我把其中的 mkfs/mkfs.c 修改了一下,就能在 windows + git bash 這樣和 mini-riscv-os 一樣的環境下編譯執行了。
|
||||||
|
|
||||||
您可以從下列網址中取得 windows 版的 xv6-riscv 原始碼,然後編譯執行看看,應該可以站在 mini-riscv-os 的基礎上,進一步透過 xv6-riscv 學習更進階的作業系統設計原理。
|
您可以從下列網址中取得 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 的學習資源,以方便大家在學習 RISC-V 作業系統設計時,不需再經過太多的摸索。
|
||||||
|
|
||||||
* [RISC-V 手册 - 一本开源指令集的指南 (PDF)](http://crva.ict.ac.cn/documents/RISC-V-Reader-Chinese-v2p1.pdf)
|
- [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)
|
- [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)
|
- [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
|
||||||
* https://github.com/riscv/riscv-opcodes/blob/master/opcodes-rv32i
|
- 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 (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)
|
- [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)
|
- 進階: [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 設計上節省一些寶貴的時間!
|
希望這份 mini-riscv-os 教材能幫助讀者在學習 RISC-V OS 設計上節省一些寶貴的時間!
|
||||||
|
|
||||||
陳鍾誠 2020/11/15 於金門大學
|
陳鍾誠 2020/11/15 於金門大學
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user