timekeeping系统为linux提供各类时间及获取函数。
timekeeping从persistent clock(rtc)获得初始时间,然后绑定clocksource,后面时间获取将通过clocksource + 各类时间的offset完成。
- /kernel/time/timekeeping.c # Time interface implementation
- /include/linux/timekeeper_internal.h
- /include/linux/timekeeping.h # Time interface declaration
- /include/linux/ktime.h # ktime_t convertion
Timekeeping系统提供了多种clock,每种clock有对应接口来获得各类时间,以下接口均返回ktime_t纳秒时间。
除了纳秒,ktime可以转换成各种时间格式,或者进行比较和计算,接口在ktime.h中定义
CLOCK_MONOTONIC
ktime_get
系统启动时间,系统睡眠停止计时
CLOCK_BOOTTIME
ktime_get_boottime
系统启动时间,系统睡眠不停止计时
CLOCK_REALTIME
ktime_get_real
Unix时间戳,不应该用于interval计算,可能被NTP更新。
CLOCK_TAI(不常用)
ktime_get_clocktai
Like ktime_get_real(), but uses the International Atomic Time (TAI) reference instead of UTC to avoid jumping on leap second updates. This is rarely useful in the kernel.
CLOCK_MONOTONIC_RAW(不常用)
ktime_get_raw
Like ktime_get(), but runs at the same rate as the hardware clocksource without (NTP) adjustments for clock drift. This is also rarely needed in the kernel.
上述所有clock,处理可以返回ktime_t,也有相应接口返回其他time数据类型
返回u64(ns)
u64 ktime_get_ns(void)
u64 ktime_get_boottime_ns(void)
u64 ktime_get_real_ns(void)
u64 ktime_get_clocktai_ns(void)
u64 ktime_get_raw_ns(void)
返回timespec64(sec + ns)
void ktime_get_ts64(struct timespec64*)
void ktime_get_boottime_ts64(struct timespec64*)
void ktime_get_real_ts64(struct timespec64*)
void ktime_get_clocktai_ts64(struct timespec64*)
void ktime_get_raw_ts64(struct timespec64*)
返回time64_t(s)
time64_t ktime_get_seconds(void)
time64_t ktime_get_boottime_seconds(void)
time64_t ktime_get_real_seconds(void)
time64_t ktime_get_clocktai_seconds(void)
time64_t ktime_get_raw_seconds(void)
系统中除了上述Clock,还提供了两个粗粒度Clock Type:
这两个Clcok速度快,精准度略低。同时也提供了相应的获取接口,类型与其他接口也保持一致。
这些时间获取接口,跳过了硬件读取过程,会节省100cycle时间,可以用于fast路径。同时会有一个tick时间的误差(250Hz,4ms)。
ktime_t ktime_get_coarse(void)
ktime_t ktime_get_coarse_boottime(void)
ktime_t ktime_get_coarse_real(void)
ktime_t ktime_get_coarse_clocktai(void)
u64 ktime_get_coarse_ns(void)
u64 ktime_get_coarse_boottime_ns(void)
u64 ktime_get_coarse_real_ns(void)
u64 ktime_get_coarse_clocktai_ns(void)
void ktime_get_coarse_ts64(struct timespec64*)
void ktime_get_coarse_boottime_ts64(struct timespec64*)
void ktime_get_coarse_real_ts64(struct timespec64*)
void ktime_get_coarse_clocktai_ts64(struct timespec64*)
与Coarse类似,但是可以用于NMI中…
u64 ktime_get_mono_fast_ns(void)
u64 ktime_get_raw_fast_ns(void)
u64 ktime_get_boot_fast_ns(void)
u64 ktime_get_tai_fast_ns(void)
u64 ktime_get_real_fast_ns(void)
下面接口已经废弃使用,需要被替换:
void ktime_get_ts(struct timespec*)
Use ktime_get()
or ktime_get_ts64()
instead.
void do_gettimeofday(struct timeval*)
void getnstimeofday(struct timespec*)
void getnstimeofday64(struct timespec64*)
void ktime_get_real_ts(struct timespec*)
ktime_get_real_ts64()
is a direct replacement, but consider using monotonic time (ktime_get_ts64()) and/or a ktime_t based interface (ktime_get()/ktime_get_real()).
struct timespec current_kernel_time(void)
struct timespec64 current_kernel_time64(void)
struct timespec get_monotonic_coarse(void)
struct timespec64 get_monotonic_coarse64(void)
These are replaced by ktime_get_coarse_real_ts64()
and ktime_get_coarse_ts64()
. However, A lot of code that wants coarse-grained times can use the simple ‘jiffies’ instead, while some drivers may actually want the higher resolution accessors these days.
struct timespec getrawmonotonic(void)
struct timespec64 getrawmonotonic64(void)
struct timespec timekeeping_clocktai(void)
struct timespec64 timekeeping_clocktai64(void)
struct timespec get_monotonic_boottime(void)
struct timespec64 get_monotonic_boottime64(void)
These are replaced by ktime_get_raw()
/ ktime_get_raw_ts64()
, ktime_get_clocktai()
/ ktime_get_clocktai_ts64()
as well as ktime_get_boottime()
/ ktime_get_boottime_ts64()
. However, if the particular choice of clock source is not important for the user, consider converting to ktime_get()
/ ktime_get_ts64()
instead for consistency.
所有time相关数据,保存在 tk_core
中(timekeeper)
struct timekeeper {
struct tk_read_base tkr_mono;
xtime_nsec // all clock offset (count by clocksource)
// the value is shifted, should >> shift
base // CLOCK_MONOTONIC base
// this + xtime_nesec >> shift = CLOCK_MONOTONIC
base_real // CLOCK_REALTIME base
// this + xtime_nesec >> shift = CLOCK_REALTIME
u64 xtime_sec; // CLOCK_REALTIME (s)
unsigned long ktime_sec; // CLOCK_MONOTONIC (s)
struct timespec64 wall_to_monotonic; // CLOCK_REALTIME + this = CLOCK_MONOTONIC
struct timespec64 monotonic_to_boot; // CLOCK_MONOTONIC + this = CLOCK_BOOTTIME
ktime_t offs_real; // CLOCK_MONOTONIC + this = CLOCK_REALTIME
ktime_t offs_boot; // CLOCK_MONOTONIC + this = CLOCK_BOOTTIME
};
timekeeping_init
timekeeping在syscore中注册了suspend/resume接口,用于保存suspend/resume时间。
suspend时,会通过rtc,以及clocksource的suspend clocksource来记录睡眠和唤醒时间,更新到tk_core的sleep time相关数据中。
timekeeping_init_ops
向syscore注册suspend、resume接口
timekeeping_suspend
分别获取睡眠时的persistent clock、clocksource counter,保存到timekeeping_suspend_time、suspend_start。
timekeeping_resume
再次获取唤醒时的persistent clock、clocksource counter,计算出睡眠时间,更新到tk_core的sleep相关数据中。
read_persistent_clock64
获取persistent clock值
clocksource_start_suspend_timing
记录clocksource counter到suspend_start变量
clocksource_stop_suspend_timing
获取suspend_start,并计算出睡眠sec返回
__timekeeping_inject_sleeptime
更新tk_core中的sleep相关成员。
update_wall_time
/ timekeeping_advance
更新tk_core到最新时间
细粒度接口的time数据,会从clocksource读取计算,粗粒度接口time数据会直接从tk_core中取得。tk_core的数据会在每个tick中得到更新。
update_wall_time
每个tick触发,从clocksource重新获取数据,并更新tk_core到最新。
tick_do_update_jiffies64
tick回调函数,更新jiffies,update tk_core到最新时间。
timekeeping提供了自己sleep time的debug信息,如果pm_debug_messages_on被打开,则在每次suspend/resume时,会打印出睡眠时间。
/debugfs/sleep_time
debugfs中也提供了文件节点,可以记录最近32次的睡眠时间
https://docs.kernel.org/core-api/timekeeping.html
https://www.cnblogs.com/arnoldlu/p/7078241.html
https://www.cnblogs.com/arnoldlu/p/7078250.html
https://www.youtube.com/watch?v=Puv4mW55bF8&ab_channel=TheLinuxFoundation