|
一次加载多个任务
|
每个app加载到内存中的base address不同
|
user/build.py每个app定制各自的base address
使用cargo rustc单独编译
|
用-Clink-args=-Text=xxxx指令ld时.text section的address是0x80400000 + app_id * 0x20000
|
|
task切换
|
与trap相比
|
不涉及特权级切换,部分编译器完成
|
对应用透明(应用不感知)
|
|
过程
|
一个app Trap进S-Mode
|
调用__switch
|
__switch返回后重新运行A
|
|
定义task context
|
ra: usize,
|
记录了 __switch 函数返回之后应该跳转到哪里继续执行
|
sp: usize,
|
stack top of current task
|
s: [usize; 12],
|
s0~s11 是被调用者保存寄存器
|
|
__switch
|
两个参数a0/x10, a1/x11,
|
当前task_cx_ptr
|
下一个task_cx_ptr
|
|
先保存current task的sp
|
a0 + 8
|
然后保存ra
|
a0
|
保存s0-12
|
|
|
ld读取 next_task_cx_ptr的同上
|
ra, s0-12, sp
|
|
|
|
|
sys_yield
|
|
多道程序执行
|
横轴时间线,纵轴是正在执行的entity
|
I/O task先执行
|
向I/O Device提交一个请求
|
开始阻塞
|
调用sys_yield让出CPU控制权
|
|
Other Task因此可以执行
|
一段时间cpu切换回I/O Task,device没有结束,继续yield
|
第二次切换,device结束,I/O Task结束
|
|
|
yield是协作式调度,task主动让出cpu
|
中间cpu会切换回蓝色应用是抢占式调度,每个task都有调度时间,防止被恶意应用饿死
|
|
|
sys_exit
|
suspend_current_and_run_next
mark_current_suspended
|
设置current的status为Ready
|
run_next_task
|
|
|
阻塞当前的运行接下来的
|
exit_current_and_run_next
|
退出当前的运行接下来的
|
|
run_next_task
使用find_nest_task寻找一个status为Ready的app获得ID
|
找不到,都执行完毕,os panic 退出
|
找到,调用__switch
|
|
|
|
|
|
TaskControlBlock
|
app的管理单位
|
现在应该是进程process的概念
|
|
TaskManager
|
|
get_num_app获得app总数
|
|
对每个app初始化
|
t.task_cx = TaskContext::goto_restore(init_app_cx(i));
|
init_app_cx向os stack压入一个TrapContext
entry是
|
APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT
|
|
sp是
|
self.data.as_ptr() as usize + USER_STACK_SIZE
|
data代表了编译时static分配的内存
|
stack pointer只需要指向一块可用的足够大的内存区域即可
|
|
|
返回压入os stack TrapContext当前的os stack的sp
|
|
goto_restore
保存传入的sp
|
是os stack的那个TrapContext的sp,如上
|
ra设置成__restore
|
|
|
Status设置Ready
|
|
|
run_first_task
|
执行第一个应用
|
|
current_task_cx_ptr给了一个TaskContext::zero_init
|
|
|
|
__switch恢复sp,sp指向init_app_cx构造的TrapContext
|
回到之前的情况
|
|
|
__restore不再需要mv sp, a0
|
__switch后,sp正确指向需要的TrapContext
|
|
|
|
|
分时多任务
时钟计数器
|
mtime
|
M-Mode的SEE预留接口
|
riscv::register::time::read()获得
|
|
mtimecmp
|
使用sbi的SBI_SET_TIMER设置
|
use crate::config::CLOCK_FREQ;
|
时钟频率,单位赫兹
|
|
1s内计数器增量
|
|
const TICKS_PER_SEC: usize = 100;
|
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
|
读取当前mtime+10ms内计数器增量
|
|
|
mtime 超过mtimecmp
|
|
触发时钟中断
|
|
|
|
|
|
riscv嵌套中断
|
|
嵌套中断是嵌套Trap的一种,Trap处理过程中发生新的Trap
|
同等特权级Mode的嵌套可以配置
|
被更高特权级Mode的Trap打断是不可避免的
|
|
默认,Trap进某个特权级,Trap处理过程中的相同特权级中断会被屏蔽
|
Trap发生,sstatus.sie保存在sstatus.spie
|
同时sstatus.sie清零,屏蔽了所有S特权级中断
|
|
Trap处理完sret,sstatus.sie从sstatus.spie恢复
|
|
如果不手动设置sstatus CSR
|
只考虑S特权级中断
|
不会嵌套中断
|
|
|
|
|
抢占式,每个app连续执行一段时间
|
内核强制切换到其他app
|
时间片Time Slice作为执行时长的度量单位
|
每个可能在毫秒量级
|
|
最简单的是RR, Round-Robin,时间片轮转算法
|
|
|
添加trap_handler分支
|
处理Trap::Interrupt(Interrupt::SupervisorTimer)
|
S特权级时钟中断
|
|
重新设置计时器set_next_trigger()
|
调用suspend_current_and_run_next,暂停当前,切换下一个
|
|
为了避免S特权级时钟中断被屏蔽
|
执行第一个app前
|
sie::set_stimer(),S-Mode时钟中断不会屏蔽
|
再设置第一个10ms计时器,set_next_trigger()
|
|
|
|
|
|
|
|
Generated
2025-05-05 04:17:35 +0000
25bcfca-dirty
2025-05-04 14:47:56 +0000