rCore 学习笔记 - 第三章

上下文切换

当前接触的上下文切换有三类:

  1. 第一类是普通的函数调用,不需要平台或者系统提供什么特殊指令,实际上用户程序自己在用户栈上操作,操作系统不用管,这是编译器的事。操作系统只需要为用户程序准备好用户栈就可以了。
  2. 第二类是由于用户程序触发异常需要到内核处理导致的,需要利用硬件的机制,保存在进入内核进行处理前,应用的运行信息,涉及到到了特权级切换和换栈的问题。
  3. 第三类是这一章新增的,切换应用导致的,现在每个程序都有单独的用户栈和内核栈(可能是因为这样比较简单),这类上下文切换和第一类函数调用的唯一区别是换栈。

时钟中断

RISC-V 的中断分为软件中断、时钟中断、外部中断三类,每类都有 Machine / Supervisor 两个版本。在 mtime 计数器到达 mtimecmp 之后就会触发一次时钟中断,时钟中断来自外部,不受处理器控制,所以对于 CPU 来说是「异步」的。

考虑中断前 CPU 特权级为 A,中断需要在特权级 B 中处理,关于中断是否被屏蔽,总结规则如下:

  1. B 高于 A,原有指令被抢占,Trap 到 B 中处理
  2. B 等于 A,以内核的 S 特权级为例,如果不屏蔽中断,首先需要将 sstatus.sie 设为 1,接着按 sie 的三个字段 ssie/stie/seie 决定是否屏蔽软件中断 / 时钟中断 / 外部中断。
  3. B 低于 A,中断被屏蔽。

作业实现

这次实验本身没什么难的,但因为我看的是 v3 的文档,测试代码用的是 2024A 的,有不少不一样的地方。比如 sys_task_info 的参数,那几个结构体的定义等。在新版的实现中似乎会让 println!("string from task info test\n"); 做两次 sys_write 的调用,卡了好久才在评论区发现,这并不是我的问题。

还有一个需要留意的地方,记录一下 Debug 过程:刚开始实现记录系统调用次数的时候,发现 assert!(3 <= info.syscall_times[SYSCALL_GETTIMEOFDAY]); 这里就爆了,打印发现值为 0. 我检查了一下代码的逻辑,没有发现问题。于是我在测试代码中完整打印了 info.syscall_times,发现个别地方是有值的。这就更奇怪了,明明有值,值只可能是内核里记录的,并且看数字大小也没什么问题。于是把有值的地方下标和值一起打印出来,发现了问题所在:下标比实际系统调用的 id 大了 1,也就是说有一个 u32 的偏移。我怀疑是系统调用的 id 写错了,检查后发现没问题,就算有问题也应当是个别偏移而不是整体偏移。我想,是不是 Rust 中 Option 的实现会带来内存布局的问题,想到内存布局问题,我就恍然大悟了。内核中在相关结构体定义时用了 #[repr(C)],而测试代码那里是没有的。


rCore 学习笔记 - 第三章
http://xiao-h.com/2025/04/08/rCore-0x03/
作者
小H
发布于
2025年4月8日
许可协议