前面四章的内容使得我们的 main.rs 变得异常冗长,代码结构也并不清晰。磨刀不误砍柴工,所以在继续编写 os 之前,我们先来对已有文件进行一些整理:
创建 lang_items.rs :
use core::panic::PanicInfo;// This function is called on panic.#[panic_handler]fnpanic(_info:&PanicInfo) ->! {println!("{}", _info);loop {}}#[no_mangle]pubexternfnabort() {panic!("abort!");}
创建 init.rs :
usecrate::interrupt::init as interrupt_init;global_asm!(include_str!("boot/entry.asm"));#[no_mangle]pubextern"C"fnrust_main() ->! {interrupt_init();println!("Hello World");unsafe{asm!("ebreak\n"::::); }panic!("End of rust_main");}
timebase 的数值约为 cpu 频率的 1% ,防止时钟中断占用过多的 cpu 资源。get_cycle 可以获取当前时间,当前时间加上 timebase(两次中断的时间差) 为下一次中断产生的时间,通过 set_timer 设置。
cpu 中有一个专门用于储存时间的 64 位寄存器。由于 system call 的返回值存放于 32 位的 x10 通用寄存器,所以需要分别读取时间的前 32 位和后 32 位:
use riscv::register::{ time, timeh};fnget_cycle() ->u64 {loop {let hi = timeh::read();let lo = time::read();let tmp = timeh::read();if (hi == tmp) {return ((hi asu64) <<32) | (lo asu64); } }}
hi 是时间的高 32 位,lo 是时间的低 32 位。注意到这里并没有之间拼接 hi 和 lo 然后将其返回,而是多了一步 if (hi == tmp) 判断。这是由于在执行完 let lo = time::read() 后,当前时间会改变。尽管时间的前 32 位改变的概率很小,但是仍然需要进行一次判断。
step2: 外设中断初始化
前面看到,时钟中断会用到 sie 寄存器。为了能够正确的产生时钟中断,我们需要先对 sie 寄存器进行初始化: