Buhe Blog - Microsoft Internet Explorer

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);
 }