batch

这章开始,计算机能处理一个接一个的任务。任务可能失败,异常,崩溃,此时 os 应该能及时处理,并开始下一个任务

栈切换

一共有 3 个栈

  • 操作系统执行之前的栈
  • 内核栈
  • 用户栈

sp 指向 boot_stack_top 执行 rust_main 执行到 run_next_app__restore 时将 sp 指向内核栈 sretsp 指向用户栈

内核栈从一开始就是空的

trap

trap 用于从用户态跳转到内核态,处理异常与中断,并在结束后返回用户态

下面考察 trap 前后的用户栈和内核栈

(栈总是要假装自己回来之后什么也没有发生)

首先,批处理系统中,不考虑外部硬件中断。因此,内核态的 CPU 不会被打断抢占

然后,不考虑分时,任务总是执行完了再执行其他任务。因此,CPU 在内核态总是把手上的事情按部就班做完,然后再慢慢回到用户态

所以当 CPU 在用户态执行程序时,内核栈只需要保存用户栈的地址,以方便在中断完成之后回到用户态

分两种情况讨论

  1. 中断
    -> 正在用户态执行任务
    -> 发生系统调用 ecall,CPU 切换到内核态
    -> 交换 sp(x2) <-> sscratch,sp 现在指向内核栈地址
-> 将寄存器信息保存到内核栈
-> 内核态处理系统调用 call trap_handler,然后回来
-> 执行 __restore
-> 指向内核栈,恢复寄存器
-> 交换 sp(x2) <-> sscratch,sp 现在指向用户栈地址
    -> sret,CPU 切换到用户态
    -> 继续执行任务
  1. 初始化
-> 在内核态,把下一个程序加载到内存
-> 伪造一份用户态寄存器上下文,
    假装自己刚刚结束了一个空的程序的系统调用,
    现在准备回用户态
-> 调用 __restore, sret 回到用户态,此时 sp 指向全新的用户栈
    -> 执行程序

sepc

Supervisor Exception Program Counter

或者 S模式异常PC寄存器

trap 发生,陷入 S 态时,sepc 被写入当前 pc 寄存器的值(通常是虚拟地址)

trap 应当保存这个值,以便 trap 结束后返回程序执行位置