第一节 获得系统时间

系统时间是支持内核运行最重要的基础之一,也是应用需要获取的重要服务之一。获取系统时间必须基于硬件平台提供的时间寄存器,它们本质上就是由晶振驱动的计数器。对 RiscV64 来说,在 M-Mode 有一个专门的计数寄存器 mtime 作为计算时间的基础,它在 S-Mode 被映射为名为 time 的寄存器,所以内核虽然运行在 S-Mode,也可以读出该寄存器的值。按照规范文档的说法,time 是 mtime 的 shadow。

既然获取系统时间是体系结构相关的功能,我们把它放到 axhal 中实现。

// axhal/src/riscv64/time.rs
use core::time::Duration;
use riscv::register::time;

const TIMER_FREQUENCY: u64 = 10_000_000;    // 10MHz
const NANOS_PER_SEC: u64 = 1_000_000_000;
const NANOS_PER_TICK: u64 = NANOS_PER_SEC / TIMER_FREQUENCY;

pub type TimeValue = Duration;

#[inline]
pub fn current_ticks() -> u64 {
    time::read() as u64
}
#[inline]
pub const fn ticks_to_nanos(ticks: u64) -> u64 {
    ticks * NANOS_PER_TICK
}
pub fn current_time_nanos() -> u64 {
    ticks_to_nanos(current_ticks())
}
pub fn current_time() -> TimeValue {
    TimeValue::from_nanos(current_time_nanos())
}

// axhal/src/riscv64.rs
pub mod time;

第18行:核心函数 current_ticks(),返回 ticks 计数值;其他函数都以它为基础进行计算。

第22行:对外提供时间服务的主要函数。无论是内核模块还是应用接口库都是经由它获取系统时间。

本节我们先来扩展 axstd,参照 Rust 官方 STD 库的实现方式,提供 Instant 类型和相关方法,向应用提供时间功能:

// axstd/src/time.rs
use core::time::Duration;
use core::ops::Sub;
use core::fmt;

#[derive(Clone, Copy)]
pub struct Instant(axhal::time::TimeValue);

impl Instant {
    pub fn now() -> Instant {
        Instant(axhal::time::current_time())
    }
    pub fn elapsed(&self) -> Duration {
        Instant::now() - *self
    }
    pub fn duration_since(&self, earlier: Instant) -> Duration {
        self.0.checked_sub(earlier.0).unwrap_or_default()
    }
}

impl Sub<Instant> for Instant {
    type Output = Duration;
    fn sub(self, other: Instant) -> Self::Output {
        self.duration_since(other)
    }
}

impl fmt::Display for Instant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}.{:06}", self.0.as_secs(), self.0.subsec_micros())
    }
}

// axstd/src/lib.rs
pub mod time;
pub use time::*;

现在,我们可以在应用 axorigin 中,测试一下获取时间的功能:

// axorigin/src/main.rs 
#![no_std]
#![no_main]

use axstd::{String, println, time};

#[no_mangle]
pub fn main(_hartid: usize, _dtb: usize) {
    let now = time::Instant::now();
    println!("\nNow: {}", now);

    let s = String::from("from String");
    println!("Hello, ArceOS![{}]", s);

    let d = now.elapsed();
    println!("Elapsed: {}.{:06}", d.as_secs(), d.subsec_micros());
}

执行 make run 测试,显示结果:

ArceOS is starting ...

Now: 0.103643 Hello, ArceOS![from String] Elapsed: 0.003917

获取时间功能验证成功!