[toc]
kernel/time/timekeeping.c 内核时间保持(Kernel Timekeeping) 维护系统时间的核心与灵魂 历史与背景 这项技术是为了解决什么特定问题而诞生的? 这项技术是为了解决操作系统中最基础、最核心的需求之一:以一种精确、稳定、单调的方式来跟踪和维护时间的流逝 。
kernel/time/timekeeping.c
所管理的时间保持子系统,具体解决了以下几个关键问题:
时间的表示与更新 :需要一个核心机制来管理系统的“官方时间”。这包括“墙上时间”(Wall Time,即CLOCK_REALTIME
,自1970年Epoch以来的时间,可以被用户或NTP修改),以及“单调时间”(Monotonic Time,即CLOCK_MONOTONIC
,从系统启动开始单调递增,不受时间调整影响)。
时钟源的抽象 :硬件提供了各种各样的时间源(Clock Sources),例如TSC(CPU的时间戳计数器)、HPET(高精度事件定时器)、ACPI PM Timer等。这些时钟源的精度、稳定性和访问速度各不相同。时间保持子系统需要一个框架来抽象这些硬件差异,并从中选择一个最佳的时钟源 来驱动系统时间。
节拍的产生与中断 :传统的操作系统依赖于周期性的“节拍中断”(Tick Interrupt)来更新时间和执行调度。但这种方式在CPU空闲时会造成不必要的唤醒和功耗。时间保持子系统需要与定时器子系统协作,支持**“动态节拍”或“无节拍”(Tickless)**模式,即只在需要时才安排下一次时钟中断。
时间的平滑调整 :当系统时间需要被调整时(例如,通过NTP进行网络时间同步),不能简单地向前或向后跳跃,因为这会导致应用程序的时间计算出现混乱。时间保持子系统必须能够以一种平滑、渐进 的方式来纠正时间偏差,例如通过在一段时间内稍微加快或减慢内核的时钟频率。
它的发展经历了哪些重要的里程碑或版本迭代? Linux的时间保持系统经历了从简单到极其复杂的演进,以追求更高的精度和效率。
基于节拍(Tick-based)的时代 :早期的Linux完全依赖于固定频率(如100Hz或1000Hz)的节拍中断。每次中断发生,时间保持代码就会将时间向前推进一个固定的增量。
高精度定时器(High-Resolution Timers, hrtimers)的引入 :这是一个重要的里程碑,使得内核可以安排纳秒级精度的定时器事件,为时间保持的精度提升奠定了基础。
动态节拍/无节拍内核(Tickless Kernel) :为了节能和降低虚拟化开销,内核引入了CONFIG_NO_HZ_IDLE
(针对空闲CPU)和CONFIG_NO_HZ_FULL
(针对被隔离用于高性能计算的CPU)。timekeeping.c
的逻辑变得更加复杂,因为它不能再依赖周期性的更新,而是需要在每次CPU进入/退出idle状态时,精确地计算并累加经过的时间。
通用时间框架(Generic Time Framework) :由Thomas Gleixner主导的对整个kernel/time
子系统的大规模重构,将时钟源(clocksource)、时钟事件设备(clockevent device)、定时器和时间保持等松散相关的部分,整合成一个清晰、健壮的框架。timekeeping.c
就是这个框架的核心。
64位时间表示 :随着时间的推移,为了避免32位时间戳在2038年溢出的问题(Y2038),内核内部的时间表示全面转向64位。
目前该技术的社区活跃度和主流应用情况如何? kernel/time/timekeeping.c
是内核中最核心、最关键的部分之一,其代码由内核时间子系统的维护者们(主要是Thomas Gleixner)进行严格的审查和管理。
绝对核心 :系统中的每一次gettimeofday()
或clock_gettime()
系统调用,每一次定时器事件,每一次调度,都直接或间接地依赖于timekeeping.c
维护的时间。
社区状态 :代码库非常成熟和稳定。社区的活跃度主要体现在:
为新的硬件架构和时钟源设备提供支持。
持续优化无节拍内核在不同负载下的精度和性能。
修复在极端情况下或与虚拟化交互时发现的精微的时间偏差问题。
核心原理与设计 它的核心工作原理是什么? timekeeping.c
的核心是基于一个选定的硬件时钟源,通过周期性或非周期性的更新,精确地维护一个多精度的时间值,并为内核其他部分提供读取时间的接口 。
时钟源(Clocksource) :
内核在启动时会探测所有可用的硬件时钟源(如TSC, HPET)。
每个时钟源都被封装在一个struct clocksource
中,该结构描述了其精度、稳定性和一个.read()
函数,用于读取硬件计数器的原始值。
时间保持子系统会根据一个评分系统,选择一个当前最佳 的时钟源来使用。
核心数据结构 (struct timekeeper
) :
所有的时间状态都集中在一个全局的struct timekeeper
变量中。
这个结构体包含了当前时间(墙上时间和单调时间)、时钟源的原始读数、用于转换的乘数和移位数(mult
和shift
)等关键信息。
时间更新 (timekeeping_update
) :
时间的更新是核心所在。当一次时钟中断发生,或者在无节拍模式下需要更新时间时,timekeeping_update()
会被调用。
它会读取当前时钟源的计数器,计算出自上次更新以来经过了多少个“周期”(cycles)。
然后,它使用timekeeper
中存储的mult
和shift
值,通过一个高精度的数学运算((cycles * mult) >> shift
),将硬件周期数精确地转换为纳秒(nanoseconds) 。
最后,将计算出的纳秒增量累加到timekeeper
中存储的当前时间上。
平滑调整(Time Slewing) :
当NTP守护进程需要调整时间时,它不会直接修改timekeeper
中的时间值。
相反,它会调整timekeeper
中的一个“频率偏移”值。timekeeping.c
的更新逻辑会在正常的增量计算之上,根据这个偏移值,额外地增加或减少一点点时间。这个微小的调整会随着时间的推移累积,从而以一种平滑的方式将系统时间逐渐“拉回”到正确的值。
它的主要优势体現在哪些方面?
高精度和高分辨率 :通过使用TSC等高频计数器和精确的数学转换,现代Linux内核的时间精度可以达到纳秒级别。
抽象和可移植性 :clocksource
框架完美地抽象了硬件差异,使得时间保持核心代码可以独立于具体的硬件平台。
效率和节能 :无节拍内核的设计,极大地降低了系统在空闲时的功耗和不必要的CPU活动。
稳定性 :平滑调整机制保证了系统时间的单调性和稳定性,对时间敏感的应用程序至关重要。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
对硬件的依赖 :时间的最终精度和稳定性,严重依赖于底层硬件时钟源的质量。一个不稳定或有bug的TSC会给整个系统带来时间问题。
虚拟化的挑战 :在虚拟机中,精确的时间保持是一个众所周知的难题。虚拟机的暂停、恢复、迁移,以及与宿主机时钟的同步,都可能导致客户机操作系统内部的时间出现跳变或不一致。timekeeping.c
中有大量复杂的代码就是为了处理这些虚拟化场景。
复杂性 :现代时间保持系统的内部逻辑极其复杂,涉及到高精度数学、原子操作、多种CPU状态(idle, nohz_full)和复杂的锁机制,是内核中最难理解和调试的部分之一。
使用场景 在哪些具体的业务或技术场景下,它是首选解决方案? 它是Linux内核中唯一且标准 的时间管理解决方案,不存在替代品。所有需要时间的内核和用户空间功能都依赖于它。
gettimeofday()
/ clock_gettime()
:所有获取当前时间的系统调用,最终都是从timekeeping.c
维护的timekeeper
结构中读取时间。
所有定时器 :无论是nanosleep
, select
的超时,还是内核内部的hrtimer
和timerfd
,它们的到期时间都是与timekeeper
维护的当前时间进行比较来判断的。
文件系统时间戳 :文件的访问时间(atime)、修改时间(mtime)、创建时间(crtime)等,都是由timekeeping.c
提供。
网络协议 :TCP协议中的时间戳选项、NTP客户端的时间同步等,都依赖于一个精确的本地时钟。
调度器 :内核调度器需要精确的时间来计算进程的运行时间片、CPU使用率等。
是否有不推荐使用该技术的场景?为什么? 这个提法不适用。在Linux内核的生态系统中,没有任何场景可以“不使用”这个核心的时间保持服务。
对比分析 请将其 与 其他相似技术 进行详细对比。 CLOCK_REALTIME
vs. CLOCK_MONOTONIC
这是由timekeeping.c
同时维护的两种最重要的时钟,理解它们的区别至关重要。
特性
CLOCK_REALTIME
CLOCK_MONOTONIC
参考点
Unix Epoch (1970-01-01 00:00:00 UTC)。
系统启动时间 。
行为
代表**“墙上时间”**或日历时间。
代表自系统启动以来单调递增 的时间。
可变性
不保证单调 。可以被settimeofday()
系统调用、NTP同步或管理员手动向前或向后调整 。
严格保证单调递增 。它绝不会向后跳跃。
受闰秒影响
是 。
否 。(有一个CLOCK_MONOTONIC_RAW
是完全不受NTP调整影响的硬件时间)。
适用场景
需要显示给用户的、与现实世界对应的日期和时间。
测量时间间隔 、设置超时、性能分析等任何不应受墙上时间变化影响的场景。这是绝大多数程序内部逻辑应使用的时钟 。
read_persistent_wall_and_boot_offset 读取持久时钟并从引导中偏移 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void __weak read_persistent_clock64 (struct timespec64 *ts) { ts->tv_sec = 0 ; ts->tv_nsec = 0 ; } void __weak __initread_persistent_wall_and_boot_offset (struct timespec64 *wall_time, struct timespec64 *boot_offset) { read_persistent_clock64(wall_time); *boot_offset = ns_to_timespec64(local_clock()); }
tk_clock_read 原子 clocksource read() 辅助函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 static inline u64 tk_clock_read (const struct tk_read_base *tkr) { struct clocksource *clock = READ_ONCE(tkr->clock); return clock->read(clock); }
tk_setup_internals 设置内部以使用 clocksource clock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 static void tk_setup_internals (struct timekeeper *tk, struct clocksource *clock) { u64 interval; u64 tmp, ntpinterval; struct clocksource *old_clock ; ++tk->cs_was_changed_seq; old_clock = tk->tkr_mono.clock; tk->tkr_mono.clock = clock; tk->tkr_mono.mask = clock->mask; tk->tkr_mono.cycle_last = tk_clock_read(&tk->tkr_mono); tk->tkr_raw.clock = clock; tk->tkr_raw.mask = clock->mask; tk->tkr_raw.cycle_last = tk->tkr_mono.cycle_last; tmp = NTP_INTERVAL_LENGTH; tmp <<= clock->shift; ntpinterval = tmp; tmp += clock->mult/2 ; do_div(tmp, clock->mult); if (tmp == 0 ) tmp = 1 ; interval = (u64) tmp; tk->cycle_interval = interval; tk->xtime_interval = interval * clock->mult; tk->xtime_remainder = ntpinterval - tk->xtime_interval; tk->raw_interval = interval * clock->mult; if (old_clock) { int shift_change = clock->shift - old_clock->shift; if (shift_change < 0 ) { tk->tkr_mono.xtime_nsec >>= -shift_change; tk->tkr_raw.xtime_nsec >>= -shift_change; } else { tk->tkr_mono.xtime_nsec <<= shift_change; tk->tkr_raw.xtime_nsec <<= shift_change; } } tk->tkr_mono.shift = clock->shift; tk->tkr_raw.shift = clock->shift; tk->ntp_error = 0 ; tk->ntp_error_shift = NTP_SCALE_SHIFT - clock->shift; tk->ntp_tick = ntpinterval << tk->ntp_error_shift; tk->tkr_mono.mult = clock->mult; tk->tkr_raw.mult = clock->mult; tk->ntp_err_mult = 0 ; tk->skip_second_overflow = 0 ; }
tk_set_wall_to_mono 更新 timekeeper 结构体中的 wall_to_monotonic 偏移量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void tk_set_wall_to_mono (struct timekeeper *tk, struct timespec64 wtm) { struct timespec64 tmp ; set_normalized_timespec64(&tmp, -tk->wall_to_monotonic.tv_sec, -tk->wall_to_monotonic.tv_nsec); WARN_ON_ONCE(tk->offs_real != timespec64_to_ktime(tmp)); tk->wall_to_monotonic = wtm; set_normalized_timespec64(&tmp, -wtm.tv_sec, -wtm.tv_nsec); WRITE_ONCE(tk->offs_real, timespec64_to_ktime(tmp)); WRITE_ONCE(tk->offs_tai, ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0 ))); }
tk_set_xtime 更新 xtime 和粗略计时器的纳秒部分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static inline void tk_update_coarse_nsecs (struct timekeeper *tk) { tk->coarse_nsec = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift; } static void tk_set_xtime (struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec = ts->tv_sec; tk->tkr_mono.xtime_nsec = (u64)ts->tv_nsec << tk->tkr_mono.shift; tk_update_coarse_nsecs(tk); }
update_fast_timekeeper 更新快速和 NMI 安全单调计时器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 static void update_fast_timekeeper (const struct tk_read_base *tkr, struct tk_fast *tkf) { struct tk_read_base *base = tkf->base; write_seqcount_latch_begin(&tkf->seq); memcpy (base, tkr, sizeof (*base)); write_seqcount_latch(&tkf->seq); memcpy (base + 1 , base, sizeof (*base)); write_seqcount_latch_end(&tkf->seq); }
timekeeping_update_from_shadow 将影子时间管理器(shadow_timekeeper)中的数据更新到实际的时间管理器(timekeeper)中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 static void timekeeping_update_from_shadow (struct tk_data *tkd, unsigned int action) { struct timekeeper *tk = &tk_core.shadow_timekeeper; lockdep_assert_held(&tkd->lock); write_seqcount_begin(&tkd->seq); if (action & TK_CLEAR_NTP) { tk->ntp_error = 0 ; ntp_clear(); } tk_update_leap_state(tk); tk_update_ktime_data(tk); update_vsyscall(tk); update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET); tk->tkr_mono.base_real = tk->tkr_mono.base + tk->offs_real; update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono); update_fast_timekeeper(&tk->tkr_raw, &tk_fast_raw); if (action & TK_CLOCK_WAS_SET) tk->clock_was_set_seq++; memcpy (&tkd->timekeeper, tk, sizeof (*tk)); write_seqcount_end(&tkd->seq); }
timekeeping_init 初始化 clocksource 和常用计时值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 static bool persistent_clock_exists;void __init timekeeping_init (void ) { struct timespec64 wall_time , /* 表示从持久时钟(persistent clock )读取的墙上时间(wall time ) */ boot_offset , /* boot_offset :表示系统启动时间的偏移量 */ wall_to_mono ; struct timekeeper *tks = &tk_core.shadow_timekeeper; struct clocksource *clock ; tkd_basic_setup(&tk_core); read_persistent_wall_and_boot_offset(&wall_time, &boot_offset); if (timespec64_valid_settod(&wall_time) && timespec64_to_ns(&wall_time) > 0 ) { persistent_clock_exists = true ; } else if (timespec64_to_ns(&wall_time) != 0 ) { pr_warn("Persistent clock returned invalid value" ); wall_time = (struct timespec64){0 }; } if (timespec64_compare(&wall_time, &boot_offset) < 0 ) boot_offset = (struct timespec64){0 }; wall_to_mono = timespec64_sub(boot_offset, wall_time); guard(raw_spinlock_irqsave)(&tk_core.lock); ntp_init(); clock = clocksource_default_clock(); if (clock->enable) clock->enable(clock); tk_setup_internals(tks, clock); tk_set_xtime(tks, &wall_time); tks->raw_sec = 0 ; tk_set_wall_to_mono(tks, wall_to_mono); timekeeping_update_from_shadow(&tk_core, TK_CLOCK_WAS_SET); }
timekeeping_get_ns 计时 get ns 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 static inline u64 timekeeping_cycles_to_ns (const struct tk_read_base *tkr, u64 cycles) { u64 mask = tkr->mask, delta = (cycles - tkr->cycle_last) & mask; if (unlikely(delta > tkr->clock->max_cycles)) { if (delta & ~(mask >> 1 )) return tkr->xtime_nsec >> tkr->shift; return delta_to_ns_safe(tkr, delta); } return ((delta * tkr->mult) + tkr->xtime_nsec) >> tkr->shift; } static __always_inline u64 timekeeping_get_ns (const struct tk_read_base *tkr) { return timekeeping_cycles_to_ns(tkr, tk_clock_read(tkr)); }
tk_fast_mono tk_fast_raw 快速单调和原始时间管理器初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #define FAST_TK_INIT \ { \ .clock = &dummy_clock, \ .mask = CLOCKSOURCE_MASK(64), \ .mult = 1, \ .shift = 0, \ } static struct tk_fast tk_fast_mono ____cacheline_aligned = { .seq = SEQCNT_LATCH_ZERO(tk_fast_mono.seq), .base[0 ] = FAST_TK_INIT, .base[1 ] = FAST_TK_INIT, }; static struct tk_fast tk_fast_raw ____cacheline_aligned = { .seq = SEQCNT_LATCH_ZERO(tk_fast_raw.seq), .base[0 ] = FAST_TK_INIT, .base[1 ] = FAST_TK_INIT, };
ktime_get 获取当前单调时间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ktime_t ktime_get (void ) { struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; ktime_t base; u64 nsecs; WARN_ON(timekeeping_suspended); do { seq = read_seqcount_begin(&tk_core.seq); base = tk->tkr_mono.base; nsecs = timekeeping_get_ns(&tk->tkr_mono); } while (read_seqcount_retry(&tk_core.seq, seq)); return ktime_add_ns(base, nsecs); } EXPORT_SYMBOL_GPL(ktime_get);
ktime_get_with_offset 获取带偏移的时间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static ktime_t *offsets[TK_OFFS_MAX] = { [TK_OFFS_REAL] = &tk_core.timekeeper.offs_real, [TK_OFFS_BOOT] = &tk_core.timekeeper.offs_boot, [TK_OFFS_TAI] = &tk_core.timekeeper.offs_tai, }; ktime_t ktime_get_with_offset (enum tk_offsets offs) { struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; ktime_t base, *offset = offsets[offs]; u64 nsecs; WARN_ON(timekeeping_suspended); do { seq = read_seqcount_begin(&tk_core.seq); base = ktime_add(tk->tkr_mono.base, *offset); nsecs = timekeeping_get_ns(&tk->tkr_mono); } while (read_seqcount_retry(&tk_core.seq, seq)); return ktime_add_ns(base, nsecs); } EXPORT_SYMBOL_GPL(ktime_get_with_offset);
ktime_get_real 以 ktime_t 格式获取真实 (wall-) 时间 1 2 3 4 5 6 7 8 9 static inline ktime_t ktime_get_real (void ) { return ktime_get_with_offset(TK_OFFS_REAL); }
ktime_get_update_offsets_now 获取当前时间并更新偏移量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 ktime_t ktime_get_update_offsets_now (unsigned int *cwsseq, ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) { struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; ktime_t base; u64 nsecs; do { seq = read_seqcount_begin(&tk_core.seq); base = tk->tkr_mono.base; nsecs = timekeeping_get_ns(&tk->tkr_mono); base = ktime_add_ns(base, nsecs); if (*cwsseq != tk->clock_was_set_seq) { *cwsseq = tk->clock_was_set_seq; *offs_real = tk->offs_real; *offs_boot = tk->offs_boot; *offs_tai = tk->offs_tai; } if (unlikely(base >= tk->next_leap_ktime)) *offs_real = ktime_sub(tk->offs_real, ktime_set(1 , 0 )); } while (read_seqcount_retry(&tk_core.seq, seq)); return base; }
do_timer 更新 jiffies 1 2 3 4 5 6 7 8 void do_timer (unsigned long ticks) { jiffies_64 += ticks; calc_global_load(); }
timekeeping_valid_for_hres 检查时间是否适合高精度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int timekeeping_valid_for_hres (void ) { struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; int ret; do { seq = read_seqcount_begin(&tk_core.seq); ret = tk->tkr_mono.clock->flags & CLOCK_SOURCE_VALID_FOR_HRES; } while (read_seqcount_retry(&tk_core.seq, seq)); return ret; }
内核时间管理:在睡眠与唤醒之间维持时间的流逝 本代码片段定义了Linux内核时间保持子系统(Timekeeping Subsystem)与系统核心电源管理框架(syscore_ops
)的接口。它的核心功能是,在系统进入睡眠(suspend)和从睡眠中唤醒(resume)这两个关键时刻,精确地管理系统时间,确保时间在操作系统“暂停”期间依然能够正确地“流逝”。
这个过程就像是一个精密的钟表匠,在需要给钟表(操作系统)上油保养(系统挂起)时,他会:
拍照留证 (timekeeping_suspend
) : 用一台高精度相机(persistent_clock
,通常是RTC)拍下当前钟表的准确时间,并记下主发条(clocksource
)停在哪个位置。然后他小心翼翼地让钟摆(tick_device
)停下来。
校准时间 (timekeeping_resume
) : 保养结束后,他重新启动钟摆。然后拿出之前拍的照片,和现在的时间对比,计算出保养期间过去了多久。他会将钟表的指针(系统时间)向前拨动这段时间,确保时间的连续性。
实现原理分析 这个过程的实现依赖于一个多源校准策略 ,因为在系统挂起期间,主要的系统时钟源(clocksource
)很可能会停止工作。内核必须有备用方案来恢复时间。
timekeeping_suspend
函数 (进入睡眠前的准备)这个函数在系统即将进入低功耗状态之前被调用,它的任务是“冻结”时间状态并做好记录。
记录“墙上时间” : read_persistent_clock64(&timekeeping_suspend_time);
是第一步。它会尝试从一个持久化时钟 (persistent clock)读取当前时间。这种时钟通常是独立于主CPU的硬件,比如一个RTC(实时时钟)芯片或SoC内部一个在低功耗模式下也能运行的计数器。这个时间戳是恢复时间的最重要依据之一。
更新内部时间 : timekeeping_forward_now(tks);
是一个内部同步函数,它确保内核的时间状态(timekeeper
结构体)与当前硬件时钟源(clocksource
)完全同步,不留下任何未处理的时间片段。
标记状态 : timekeeping_suspended = 1;
设置一个全局标志,通知内核的其他部分时间子系统已暂停。
准备“不停歇”时钟 : clocksource_start_suspend_timing(...)
通知当前的时钟源,“我们要睡了,如果你是那种能在睡眠中继续计数的特殊时钟(suspend-nonstop clocksource),请开始记录”。这是一种优化,如果存在这种时钟,恢复时的精度会非常高。
漂移补偿 : 接下来的if (persistent_clock_exists)
块是一个非常精妙的防漂移算法 。
问题 : 每次挂起/恢复,由于各种延迟,可能会引入微小的误差(比如接近1秒)。如果频繁挂起/恢复,这种误差会累积,导致系统时间与真实的墙上时间(由RTC等提供)越差越远。
解决方案 : 内核会记住上一次挂起时,系统时间与持久化时钟之间的差值(old_delta
)。在本次挂起时,它会再次计算这个差值(delta
),并比较两次差值的变化(delta_delta
)。如果变化不大,就认为这是累积误差,并微调本次记录的挂起时间戳(timekeeping_suspend_time
),从而“吸收”掉这个误差,防止它传递到下一次。
关闭硬件 : tick_suspend()
, clocksource_suspend()
, clockevents_suspend()
会依次调用驱动程序,让节拍定时器(tick device)、时钟源等硬件进入低功耗模式。
timekeeping_resume
函数 (从睡眠中唤醒后的校准)这个函数在系统刚刚从低功耗状态唤醒后被调用,它的任务是计算并补偿睡眠时间。
读取“墙上时间” : read_persistent_clock64(&ts_new);
,和挂起时一样,再次读取持久化时钟,获取当前的准确时间。
开启硬件 : clockevents_resume()
, clocksource_resume()
首先唤醒底层的时间硬件,让它们准备好工作。
计算睡眠时间 (核心) : 这是整个恢复过程的核心,它按优先级顺序尝试三种方法来计算睡眠时间:
方法一 (最佳): 不停歇时钟源 : clocksource_stop_suspend_timing(clock, cycle_now)
会询问时钟源:“你睡着了吗?”如果时钟源回答:“没有,我一直在数,这是我睡着期间数到的总数”,那么内核就得到了最精确的睡眠时间(nsec > 0
)。
方法二 (次佳): 持久化时钟 : 如果方法一失败(时钟源睡着了),内核就会使用ts_delta = timespec64_sub(ts_new, timekeeping_suspend_time);
,即用“现在墙上时间”减去“睡觉前记录的墙上时间”来计算睡眠时长。
方法三 (备用): RTC核心 : 如果前两种方法都失败了,还有一个备用方案,由RTC子系统的核心代码在稍晚的时候通过RTC硬件来校准时间(代码中未体现,但注释中提到了)。
注入睡眠时间 : __timekeeping_inject_sleeptime(tks, &ts_delta);
将计算出的睡眠时长ts_delta
“注入”到内核时间保持器中,也就是将系统时间向前拨动相应的时长。
重置状态和重启服务 :
重置cycle_last
等内部状态变量。
timekeeping_suspended = 0;
清除暂停标志。
tick_resume()
和timerfd_resume()
等函数会重新启动周期性的节拍中断,并通知所有等待时间的任务(如timerfd
)时间可能发生了跳变。
代码分析 timekeeping_resume
函数此函数在系统从挂起状态恢复后被调用,负责校准系统时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 void timekeeping_resume (void ) { struct timekeeper *tks = &tk_core.shadow_timekeeper; struct clocksource *clock = tks->tkr_mono.clock; struct timespec64 ts_new , ts_delta ; bool inject_sleeptime = false ; u64 cycle_now, nsec; unsigned long flags; read_persistent_clock64(&ts_new); clockevents_resume(); clocksource_resume(); raw_spin_lock_irqsave(&tk_core.lock, flags); cycle_now = tk_clock_read(&tks->tkr_mono); nsec = clocksource_stop_suspend_timing(clock, cycle_now); if (nsec > 0 ) { ts_delta = ns_to_timespec64(nsec); inject_sleeptime = true ; } else if (timespec64_compare(&ts_new, &timekeeping_suspend_time) > 0 ) { ts_delta = timespec64_sub(ts_new, timekeeping_suspend_time); inject_sleeptime = true ; } if (inject_sleeptime) { suspend_timing_needed = false ; __timekeeping_inject_sleeptime(tks, &ts_delta); } tks->tkr_mono.cycle_last = cycle_now; tks->tkr_raw.cycle_last = cycle_now; tks->ntp_error = 0 ; timekeeping_suspended = 0 ; timekeeping_update_from_shadow(&tk_core, TK_CLOCK_WAS_SET); raw_spin_unlock_irqrestore(&tk_core.lock, flags); touch_softlockup_watchdog(); tick_resume(); timerfd_resume(); }
timekeeping_suspend
函数此函数在系统即将挂起前被调用,负责保存时间状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 int timekeeping_suspend (void ) { struct timekeeper *tks = &tk_core.shadow_timekeeper; struct timespec64 delta , delta_delta ; static struct timespec64 old_delta ; struct clocksource *curr_clock ; unsigned long flags; u64 cycle_now; read_persistent_clock64(&timekeeping_suspend_time); if (timekeeping_suspend_time.tv_sec || timekeeping_suspend_time.tv_nsec) persistent_clock_exists = true ; suspend_timing_needed = true ; raw_spin_lock_irqsave(&tk_core.lock, flags); timekeeping_forward_now(tks); timekeeping_suspended = 1 ; curr_clock = tks->tkr_mono.clock; cycle_now = tks->tkr_mono.cycle_last; clocksource_start_suspend_timing(curr_clock, cycle_now); if (persistent_clock_exists) { delta = timespec64_sub(tk_xtime(tks), timekeeping_suspend_time); delta_delta = timespec64_sub(delta, old_delta); if (abs (delta_delta.tv_sec) >= 2 ) { old_delta = delta; } else { timekeeping_suspend_time = timespec64_add(timekeeping_suspend_time, delta_delta); } } timekeeping_update_from_shadow(&tk_core, 0 ); halt_fast_timekeeper(tks); raw_spin_unlock_irqrestore(&tk_core.lock, flags); tick_suspend(); clocksource_suspend(); clockevents_suspend(); return 0 ; }
syscore_ops
结构体与初始化函数这部分是将上述功能注册到内核电源管理框架的“胶水”代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static struct syscore_ops timekeeping_syscore_ops = { .resume = timekeeping_resume, .suspend = timekeeping_suspend, }; static int __init timekeeping_init_ops (void ) { register_syscore_ops(&timekeeping_syscore_ops); return 0 ; } device_initcall(timekeeping_init_ops);