需要实现什么
本章的目标是实现分时多任务系统,它能并发地执行多个用户程序,并调度这些程序。为此需要实现
一次性加载所有用户程序,减少任务切换开销;
支持任务切换机制,保存切换前后程序上下文;
支持程序主动放弃处理器,实现 yield 系统调用;
以时间片轮转算法调度用户程序,实现资源的时分复用。
拥有什么
实现了用户程序加载、用户态与内核态间的相互转换、trap处理机制、用户程序运行完成后切换至下一个程序的批处理系统。
思路
区别:
- 程序保存在内存中而非作为数据段链接到内核里。
- 同时管理多个应用程序。
定时触发时钟中断:
通过M特权级获取时间 -> 设置mtimecmp触发中断 -> 捕获中断运行下一个app
应用程序管理:
单例管理所有程序 -> 将程序保存在内存中 -> 实现应用加载、切换、退出 -> 实现yield让步
本章代码树
── os
├── build.rs
├── Cargo.toml
├── Makefile
└── src
├── batch.rs(移除:功能分别拆分到 loader 和 task 两个子模块)
├── config.rs(新增:保存内核的一些配置)
├── console.rs
├── logging.rs
├── sync
├── entry.asm
├── lang_items.rs
├── link_app.S
├── linker.ld
├── loader.rs(新增:将应用加载到内存并进行管理)
├── main.rs(修改:主函数进行了修改)
├── sbi.rs(修改:引入新的 sbi call set_timer)
├── syscall(修改:新增若干 syscall)
│ ├── fs.rs
│ ├── mod.rs
│ └── process.rs
├── task(新增:task 子模块,主要负责任务管理)
│ ├── context.rs(引入 Task 上下文 TaskContext)
│ ├── mod.rs(全局任务管理器和提供给其他模块的接口)
│ ├── switch.rs(将任务切换的汇编代码解释为 Rust 接口 __switch)
│ ├── switch.S(任务切换的汇编代码)
│ └── task.rs(任务控制块 TaskControlBlock 和任务状态 TaskStatus 的定义)
├── timer.rs(新增:计时器相关)
└── trap
├── context.rs
├── mod.rs(修改:时钟中断相应处理)
└── trap.S
实现过程
定时触发时钟中断
通过M特权级获取时间
// os/src/timer.rs
use riscv::register::time;
pub fn get_time() -> usize {
time::read()
}
设置mtimecmp触发中断
RISC-V 要求处理器维护时钟计数器 mtime,还有另外一个 CSR mtimecmp 。 一旦计数器 mtime 的值超过了 mtimecmp,就会触发一次时钟中断。
// os\src\sbi.rs
/// use sbi call to set timer
pub fn set_timer(timer: usize) {
sbi_call(SBI_SET_TIMER, timer, 0, 0);
}
捕获中断运行下一个app
重新设置mtimecmp后运行下一个app。
timer 子模块的 set_next_trigger 函数对 set_timer 进行了封装, 它首先读取当前 mtime 的值,然后计算出 10ms 之内计数器的增量,再将 mtimecmp 设置为二者的和。 这样,10ms 之后一个 S 特权级时钟中断就会被触发。
trap_handler详解见 ch2 。
// os\src\trap\mod.rs
/// trap handler
#[no_mangle]
pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
...
Trap::Interrupt(Interrupt::SupervisorTimer) => {
set_next_trigger();
suspend_current_and_run_next();
}
...
}
// os\src\timer.rs
/// Set the next timer interrupt
pub fn set_next_trigger() {
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
}
应用程序管理
单例统一管理所有应用程序
TaskManager保存任务(进程)数、所有任务信息task、当前任务id。
Task包括任务状态和任务上下文TaskContext。
TaskContext包括ra、sp、s0-11寄存器。
lazy_static! {
/// Global variable: TASK_MANAGER
pub static ref TASK_MANAGER: TaskManager = {
let num_app = get_num_app();
let mut tasks = [TaskControlBlock {
task_cx: TaskContext::zero_init(),
task_status: TaskStatus::UnInit,
syscall_t