|
|
| 一次加载多个任务
|
| 每个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