OS 简单教程十一OS 简单教程十一
🌁

OS 简单教程十一

__switch

 .altmacro  .macro SAVE_SN n      sd s\n, (\n+1)*8(sp)  .endm  .macro LOAD_SN n      ld s\n, (\n+1)*8(sp)  .endm      .section .text     .globl __switch  __switch:      # __switch(      #     current_task_cx_ptr2: &*const TaskContext,      #     next_task_cx_ptr2: &*const TaskContext      # )      # push TaskContext to current sp and save its address to where a0 points to      addi sp, sp, -13*8      # sd 是寄存器到内存      sd sp, 0(a0)      # fill TaskContext with ra & s0-s11      sd ra, 0(sp)      .set n, 0      .rept 12          SAVE_SN %n          .set n, n + 1     .endr      # ready for loading TaskContext a1 points to      # a1 -> sp      ld sp, 0(a1)      # load registers in the TaskContext      ld ra, 0(sp)      .set n, 0      .rept 12          LOAD_SN %n          .set n, n + 1     .endr      # pop TaskContext      addi sp, sp, 13*8      ret
__switch 函数主要保存被调用者的寄存器到内存中的 TaskContext 中,而后改变 sp 和 ra 寄存器。
  • sp 寄存器指定了新任务的堆栈位置
  • ra 寄存器其实是 return address ,函数返回的地址,指定了新任务下一步要执行的指令
切换了它们两个寄存器,就切换了任务。

trap_return

 #[no_mangle]  pub fn trap_return() -> ! {      set_user_trap_entry();      let trap_cx_ptr = TRAP_CONTEXT;      let user_satp = current_user_token();      extern "C" {          fn __alltraps();          fn __restore();     }      let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;      unsafe {          asm!(              "fence.i",              "jr {restore_va}",              restore_va = in(reg) restore_va,              in("a0") trap_cx_ptr,              in("a1") user_satp,              options(noreturn)         );     }      // panic!("Unreachable in back_to_user!");  }
为什么不直接调用 __restore 呢?
因为 TrapContext 在应用的虚拟内存里,要拿到应用的 token 和 TrapContext 的虚拟地址,__restore 先通过 token 切换地址空间再访问 TrapContext 。

抢占式调度

为什么使用抢占式调度?协作就像靠自觉,抢占就像排队,排队整体效率提高了,虽然牺牲了个人利益。
抢占式调度采用时钟中断实现,时钟中断就是常说的时间片,时钟中断其实是 SBI 提供的。到了固定的时间触发时钟中断,进而进入中断处理程序,中断处理程序发现是时钟中断,切换到下一个任务来完成抢占式调度。
 pub fn get_time() -> usize {      time::read()  }  pub fn get_time_ms() -> usize {      time::read() / (CLOCK_FREQ / MSEC_PER_SEC)  }  pub fn set_next_trigger() {      set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);  }