batch
这章开始,计算机能处理一个接一个的任务。任务可能失败,异常,崩溃,此时 os 应该能及时处理,并开始下一个任务
栈切换
一共有 3 个栈
- 操作系统执行之前的栈
- 内核栈
- 用户栈
sp
指向boot_stack_top
执行rust_main
执行到run_next_app
,__restore
时将sp
指向内核栈sret
,sp
指向用户栈
内核栈从一开始就是空的
trap
trap 用于从用户态跳转到内核态,处理异常与中断,并在结束后返回用户态
下面考察 trap 前后的用户栈和内核栈
(栈总是要假装自己回来之后什么也没有发生)
首先,批处理系统中,不考虑外部硬件中断。因此,内核态的 CPU 不会被打断抢占
然后,不考虑分时,任务总是执行完了再执行其他任务。因此,CPU 在内核态总是把手上的事情按部就班做完,然后再慢慢回到用户态
所以当 CPU 在用户态执行程序时,内核栈只需要保存用户栈的地址,以方便在中断完成之后回到用户态
分两种情况讨论
- 中断
-> 正在用户态执行任务
-> 发生系统调用 ecall,CPU 切换到内核态
-> 交换 sp(x2) <-> sscratch,sp 现在指向内核栈地址
-> 将寄存器信息保存到内核栈
-> 内核态处理系统调用 call trap_handler,然后回来
-> 执行 __restore
-> 指向内核栈,恢复寄存器
-> 交换 sp(x2) <-> sscratch,sp 现在指向用户栈地址
-> sret,CPU 切换到用户态
-> 继续执行任务
- 初始化
-> 在内核态,把下一个程序加载到内存
-> 伪造一份用户态寄存器上下文,
假装自己刚刚结束了一个空的程序的系统调用,
现在准备回用户态
-> 调用 __restore, sret 回到用户态,此时 sp 指向全新的用户栈
-> 执行程序
sepc
Supervisor Exception Program Counter
或者 S模式异常PC寄存器
当 trap
发生,陷入 S
态时,sepc
被写入当前 pc
寄存器的值(通常是虚拟地址)
trap
应当保存这个值,以便 trap
结束后返回程序执行位置