RCore-腔骨龙

总体结构

腔骨龙分时多任务操作系统 -- TimesharingOS总体结构

背景知识

与之相对, 抢占式调度 (Preemptive Scheduling) 则是应用 随时 都有被内核切换出去的可能。现代的任务调度算法基本都是抢占式的,它要求每个应用只能连续执行一段时间,然后内核就会将它强制性切换出去。

本章中我们仅需要最原始的 RR 算法,用文字描述的话就是维护一个任务队列,每次从队头取出一个应用执行一个时间片,然后把它丢到队尾,再继续从队头取出一个应用,以此类推直到所有的应用执行完毕。

在 RISC-V 架构语境下, 中断 (Interrupt) 和我们第二章中介绍的异常(包括程序错误导致或执行 Trap 类指令如用于系统调用的 ecall )一样都是一种 Trap ,但是它们被触发的原因却是不同的。对于某个处理器核而言, 异常与当前 CPU 的指令执行是 同步 (Synchronous) 的,异常被触发的原因一定能够追溯到某条指令的执行;而中断则 异步 (Asynchronous) 于当前正在进行的指令,也就是说中断来自于哪个外设以及中断如何触发完全与处理器正在执行的当前指令无关。

在不考虑指令集拓展的情况下,RISC-V 架构中定义了如下中断:

RISC-V 中断一览表

Interrupt Exception Code Description
1 1 Supervisor software interrupt
1 3 Machine software interrupt
1 5 Supervisor timer interrupt
1 7 Machine timer interrupt
1 9 Supervisor external interrupt
1 11 Machine external interrupt

RISC-V 的中断可以分成三类:

  • 软件中断 (Software Interrupt):由软件控制发出的中断

  • 时钟中断 (Timer Interrupt):由时钟电路发出的中断

  • 外部中断 (External Interrupt):由外设发出的中断

中断屏蔽 CSR

书上写得有些简单,以下为我的理解:

  • sstatus (Supervisor Status Register)

    • sstatus 寄存器管理处理器的状态,包括中断使能位。
    • 关键位:
      • **SIE (Supervisor Interrupt Enable)**:控制全局中断使能。当 SIE 为 1 时,允许中断;为 0 时,禁止中断。
  • sie (Supervisor Interrupt Enable Register)

    • sie 寄存器控制特定类型中断的使能。
    • 关键位:
      • **SSIE (Supervisor Software Interrupt Enable)**:使能软件中断。
      • **STIE (Supervisor Timer Interrupt Enable)**:使能定时器中断。
      • **SEIE (Supervisor External Interrupt Enable)**:使能外部中断。
  • 中断处理流程

    1. 中断触发:当中断条件满足时,中断被触发。
    2. 全局中断使能检查:处理器检查 sstatus.SIE,若为 1,继续处理中断。
    3. 特定中断使能检查:处理器检查 sie 中相应中断类型的使能位,若为 1,处理该中断。
    4. 中断处理:处理器保存上下文,跳转到中断处理程序。

在目前的系统中,只需要知道:

  • U 特权级的应用程序发出系统调用或产生错误异常都会跳转到 S 特权级的操作系统内核来处理;
  • S 特权级的时钟/软件/外部中断产生后,都会跳转到 S 特权级的操作系统内核来处理。

中断产生后,硬件会完成如下事务:

  • 当中断发生时,sstatus.sie 字段会被保存在 sstatus.spie 字段中,同时把 sstatus.sie 字段置零,这样软件在进行后续的中断处理过程中,所有 S 特权级的中断都会被屏蔽;(不存在嵌套中断)

  • 当软件执行中断处理完毕后,会执行 sret 指令返回到被中断打断的地方继续执行,硬件会把 sstatus.sie 字段恢复为 sstatus.spie 字段内的值。

代码

timer

和前一个操作系统相比新增了时钟。在timer.rs中对外提供接口获取相关时间信息:

1
2
pub fn get_time() -> usize
pub fn get_time_ms() -> usize

这两个函数用于获取时间,关键在于触发时钟中断的函数:

1
2
3
4
/// set the next timer interrupt
pub fn set_next_trigger() {
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
}

set_timer用于设置mtimecmp。riscv 中使用 mtime来从上电以来经过多少时钟周期。当mtime 大于mtimecmp,就会触发时钟中断

trap

硬件已经帮我们完成了很多工作了,现在看看内核怎么处理中断:

1
2
3
4
Trap::Interrupt(Interrupt::SupervisorTimer) => {
set_next_trigger();
suspend_current_and_run_next();
}

trap_handler的分发中加入了来自S等级的时钟中断的判断:重新设置计时器,暂停当前的 task 并切换到下一个

比较简单,接下来看我们如何超越物理内存!


RCore-腔骨龙
https://pactheman123.github.io/2025/01/24/RCore-腔骨龙/
作者
Xiaopac
发布于
2025年1月24日
许可协议