Open utterances-bot opened 2 years ago
编程题 3. 编写浮点应用程序A
请问什么是“浮点应用程序”呢?是类似 ./user/src/bin/01power_5.rs
这样,然后随意计算一个浮点运算任务么?
unsafe fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext) {
SWITCH_TIME_START = get_time_us();
switch::__switch(current_task_cx_ptr, next_task_cx_ptr);
SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
}
编程题4的此处,为什么__switch
跳完栈切换完指令后,仍能完成增加计时的运算?
2.4 第三点的描述感觉不对
通过时钟中断切换任务: ... 切换到新任务后,在 trap 结尾处遇到函数 user_time_start(),刷新停表,并统计新任务的内核态时间
按照 ch3 的实现,__switch 函数通过直接恢复ra来切到新任务,不会返回到trap的结尾
@kayoch1n 没问题呀,假设是两个任务轮流执行的情况,一个任务时间片用尽进入trap,调用suspend_current_and_run_next
,最终调用__switch
切换到另一个任务。另一个任务时间片也用尽,通过__switch
切换回来的时候,要切换回的ra其实是调用__switch
的指令的下一条指令,所以会从__switch
返回的地方继续执行,最后走到trap结尾。再看看__switch
对ra的处理吧。
@kayoch1n 没问题呀,假设是两个任务轮流执行的情况,一个任务时间片用尽进入trap,调用
suspend_current_and_run_next
,最终调用__switch
切换到另一个任务。另一个任务时间片也用尽,通过__switch
切换回来的时候,要切换回的ra其实是调用__switch
的指令的下一条指令,所以会从__switch
返回的地方继续执行,最后走到trap结尾。再看看__switch
对ra的处理吧。
ok了,我的代码有点问题,修改之后是可以返回并且走到trap结尾的
关于第三个问题支持浮点运算,我觉得可以提一下需要事先将 sstatus.fs 设置为一个非0的值,否则尽管编译target包含了浮点指令拓展、程序首次执行浮点指令的时候会抛出illegal instruction异常。
这里3.1.6.6有提到这个点,而且我自己在qemu上试了一下确实会直接抛出异常,设置成1之后可解决这个首次执行异常的问题
When an extension’s status is set to Off, any instruction that attempts to read or write the corresponding state will cause an illegal instruction exception.
unsafe fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext) { SWITCH_TIME_START = get_time_us(); switch::__switch(current_task_cx_ptr, next_task_cx_ptr); SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START; }
编程题4的此处,为什么
__switch
跳完栈切换完指令后,仍能完成增加计时的运算?
他这里的答案有问题,__switch
之后会跳转到 __restore
恢复到用户态,后面那句只有下一次用户态 trap 后才会执行。 虽然不对我还是测试了一下, 发现 context switch 要花费几百毫秒, 这肯定是不可能的。 可行的办法是在 goto_restore
中更改入口地址:
这里我的测试可能是出了问题,见后续评论与Mars的讨论。
pub fn goto_restore(kstack_ptr: usize) -> Self {
extern "C" {
fn __pre_restore();
}
Self {
ra: __pre_restore as usize,
sp: kstack_ptr,
s: [0; 12],
}
}
之后在 trap.S
末尾加上
__pre_restore:
mv a0, sp
call switch_cost
mv sp, a0
j __restore
计时的函数是
// os/src/task/mod.rs
pub static mut SWITCH_TASK_START: usize = 0;
pub unsafe fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext) {
SWITCH_TASK_START = get_time_us();
switch::__switch(current_task_cx_ptr, next_task_cx_ptr);
crate::task::update_switch_cost(get_time_us() - SWITCH_TASK_START);
}
...
pub fn update_switch_cost(cost: usize) {
let mut inner = TASK_MANAGER.inner.exclusive_access();
let current = inner.current_task;
inner.tasks[current].switch_time += cost;
}
// os/src/trap/mod.rs
#[no_mangle]
pub unsafe extern "C" fn switch_cost (cx: &mut TrapContext) -> &mut TrapContext {
crate::task::update_switch_cost(get_time_us() - SWITCH_TASK_START);
cx
}
TaskControlBlock
中要增加 pub switch_time: usize
。
"虽然不对我还是测试了一下, 发现 context switch 要花费几百毫秒, 这肯定是不可能的。 ", 我的测试结果: task switch time: 103 us
"编程题4的此处,为什么switch跳完栈切换完指令后,仍能完成增加计时的运算?" "他这里的答案有问题,switch 之后会跳转到 __restore 恢复到用户态,后面那句只有下一次用户态 trap 后才会执行。 虽然不对我还是测试了一下, 发现 context switch 要花费几百毫秒, 这肯定是不可能的。"
我是这样理解的:
// os/src/task/switch.rs
extern "C" {
pub fn __switch(
current_task_cx_ptr: *mut TaskContext,
next_task_cx_ptr: *const TaskContext
);
}
unsafe fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext) {
println!("\x1b[31m Start switching from current_task_cx_ptr={:#?} to next_task_cx_ptr={:#?} \x1b[0m ", current_task_cx_ptr, next_task_cx_ptr);
SWITCH_TIME_START = get_time_us();
switch::__switch(current_task_cx_ptr, next_task_cx_ptr);
SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
println!("\x1b[31m Switch back!!! current_task_cx_ptr={:#?}, next_task_cx_ptr={:#?} \x1b[0m ", current_task_cx_ptr, next_task_cx_ptr);
}
SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
, 所以, 这个 task switch 时间被记录下来了@HangX-Ma
SWITCH_TIME_START = get_time_us();
这就更新了 SWITCH_TIME_START
, 然后 switch 到 task A 去 运行 SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
, 这就记录了 B switch to A 的时间; 2. 如果 task B 不是第一次运行,只是 suspend, 那么 A switch to B 后会直接运行 task B 的 SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
, 这就记录了 A switch to B的时间@HangX-Ma
“此时本该记录context switch的开销”, 你的代码解决了这个问题,是没错,看我之前的回复
“那么在你下一次执行__switch从taskB切换到taskA,记录COUNT的时候,实际上把taskB的运行时间也计算进去了。”, 1. 如果task B是 第一次运行,那么 task B 会从用户态trap, task B 会再次运行
SWITCH_TIME_START = get_time_us();
这就更新了SWITCH_TIME_START
, 然后 switch 到 task A 去 运行SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
, 这就记录了 B switch to A 的时间; 2. 如果 task B 不是第一次运行,只是 suspend, 那么 A switch to B 后会直接运行 task B 的SWITCH_TIME_COUNT += get_time_us() - SWITCH_TIME_START;
, 这就记录了 A switch to B的时间
确实是会更新,回复完就想到了。我之前的代码少了对后续的switch的记录,这点确实有问题。
@HangX-Ma 你的学习笔记写的很好,我是对着你的学习笔记来学 rCore 的,如果我的实现有什么问题,欢迎直接到我的repo 下提 issue 或者 PR https://github.com/OccupyMars2025/reimplement-rCore-Tutorial-v3-from-scratch/commit/09f18069d5ab13f7c941648ef4cd6a6f611733bd
@HangX-Ma 你的学习笔记写的很好,我是对着你的学习笔记来学 rCore 的,如果我的实现有什么问题,欢迎直接到我的repo 下提 issue 或者 PR https://github.com/OccupyMars2025/reimplement-rCore-Tutorial-v3-from-scratch/commit/09f18069d5ab13f7c941648ef4cd6a6f611733bd
谢谢认可,笔记主要还是厘清思路。个人学习难免有所缺漏,多多讨论好处良多。
另外,虽然rCore仅有这本导读的评论区作为交流平台时效性差了些,但大家的留言都很有质量,收获很多。希望到时候官方能提供更好的交流平台。(错过了一年一度的训练营也能交流的那种hh)
@HangX-Ma 您好,您的笔记是在什么平台分享的, 在进行学习rcore过程中,有些地方比较难看懂,希望看下你的笔记
@HangX-Ma 您好,您的笔记是在什么平台分享的, 在进行学习rcore过程中,有些地方比较难看懂,希望看下你的笔记
GitPage,我的github主页点开就看得到🤣不过后面两张比较草率写了框架没写个人想法,最近准备把rCore捡起来再看看。
雀氏,必须先把sstatus的fs字段设置为10 或 11 才能保存浮点寄存器 o^o
练习参考答案 - rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档
https://rcore-os.github.io/rCore-Tutorial-Book-v3/chapter3/6answer.html