kernel/time 内核时间与定时器机制与原理详解
源码路径:rk3588/kernel-6.1/kernel/time/
内核版本:Linux 6.1(RK3588 平台)
平台:RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE,ARM64)
该目录实现 Linux 内核 时间子系统核心:wall clock 维护(timekeeping)、时钟源/时钟事件设备管理、jiffies、调度时钟 sched_clock()、高精度定时器(hrtimer)、低精度定时器轮(timer wheel)、POSIX 定时器、NTP 调整、NO_HZ tickless idle、alarmtimer 以及 VDSO 时间页更新。
定位:内核「时间基础设施」;硬件时钟源驱动在 drivers/clocksource/(如 ARM Generic Timer),架构入口在 arch/arm64/。
目录
一、时间子系统概览
Linux 时间子系统分为 四个层次:
| 层次 |
职责 |
典型组件 |
| 硬件 |
提供自由运行计数器与可编程定时中断 |
ARM Generic Timer(CNTPCT/CNTVCT) |
| 抽象层 |
注册/选择 clocksource 与 clockevent |
clocksource.c / clockevents.c |
| 时间维护 |
将硬件 tick 转换为 wall clock / monotonic |
timekeeping.c / ntp.c |
| 定时器 API |
内核/用户态定时需求 |
hrtimer / timer wheel / POSIX timers |
两类硬件抽象:
- Clocksource(时钟源):只读、单调递增,用于 读当前时间(如
ktime_get())
- Clockevent(时钟事件):可编程,用于 产生 tick 中断(调度、hrtimer、jiffies 推进)
二、源码目录结构
2.1 编译依赖(Makefile)
1 2 3 4 5 6 7 8 9 10 11
| obj-y += time.o timer.o hrtimer.o obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o obj-y += timeconv.o timecounter.o alarmtimer.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS) += clockevents.o tick-common.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += tick-broadcast.o obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o tick-sched.o obj-$(CONFIG_GENERIC_SCHED_CLOCK) += sched_clock.o obj-$(CONFIG_HAVE_GENERIC_VDSO) += vsyscall.o obj-$(CONFIG_POSIX_TIMERS) += posix-timers.o posix-cpu-timers.o ... obj-$(CONFIG_TIME_NS) += namespace.o
|
| 文件 |
作用 |
timekeeping.c |
核心时间维护:xtime、monotonic、raw、fast timekeeper |
clocksource.c |
时钟源注册、rating 选择、watchdog |
clockevents.c |
时钟事件设备管理、状态切换 |
hrtimer.c |
高精度 per-CPU 红黑树定时器 |
timer.c |
多级 timer wheel 低精度定时器 |
tick-common.c |
tick 设备、do_timer()、jiffies 推进协调 |
tick-sched.c |
NO_HZ / HIGH_RES_TIMERS tick 调度 |
tick-broadcast.c |
深度 idle CPU 的 broadcast tick |
sched_clock.c |
扩展 sched_clock() 到 64 位 ns |
ntp.c |
NTP PLL 频率/相位调整 |
time.c |
gettimeofday / clock_settime / adjtimex 等 syscall |
posix-timers.c |
timer_create / clock_nanosleep 等 |
alarmtimer.c |
可唤醒系统的 alarm 定时器 |
vsyscall.c |
VDSO datapage 更新(ARM64 通用实现) |
三、Kconfig 与 RK3588 默认配置
3.1 关键 Kconfig 选项
| 配置项 |
说明 |
CONFIG_GENERIC_CLOCKEVENTS |
通用 clockevent 框架(非 LEGACY) |
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST |
深度 idle 时 broadcast tick |
CONFIG_TICK_ONESHOT |
单次触发 clockevent(hrtimer/NO_HZ 依赖) |
CONFIG_NO_HZ_IDLE |
Tickless idle(默认 NO_HZ=y 时) |
CONFIG_NO_HZ_FULL |
全动态 tick(需 CPU 隔离) |
CONFIG_HIGH_RES_TIMERS |
高精度 hrtimer |
CONFIG_GENERIC_SCHED_CLOCK |
通用 sched_clock 扩展 |
CONFIG_HAVE_GENERIC_VDSO |
VDSO 时间页 |
CONFIG_POSIX_TIMERS |
POSIX 定时器 |
CONFIG_TIME_NS |
时间命名空间 |
3.2 RK3588 Rockchip defconfig
以 arch/arm64/configs/rockchip_linux_defconfig 为例:
1 2
| CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y
|
ARM64 Kconfig 还 select:
GENERIC_CLOCKEVENTS_BROADCAST — big.LITTLE 深 idle 时需要 broadcast
ARCH_HAS_TICK_BROADCAST
HAVE_GENERIC_VDSO — 用户态 clock_gettime 走 VDSO
含义:RK3588 启用 tickless idle + 高精度定时器;空闲时停止 periodic tick 以省电,唤醒时由 hrtimer/clockevent 补偿 jiffies。
四、整体架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ┌─────────────────────────────────────────────────────────────┐ │ drivers/clocksource/ │ │ arm_arch_timer.c (CNTPCT, ~24MHz) │ └───────────────┬─────────────────────────┬───────────────────┘ │ clocksource │ clockevent (PPI) ▼ ▼ ┌───────────────────────┐ ┌──────────────────────────────┐ │ clocksource.c │ │ clockevents.c + tick-*.c │ │ curr_clocksource │ │ tick_handler → hrtimer │ └───────────┬───────────┘ └──────────────┬───────────────┘ │ │ ▼ ▼ ┌───────────────────────────────────────────────────────────┐ │ timekeeping.c │ │ tk_core (seqlock) + tk_fast (NMI-safe latch) │ │ xtime_sec / wall_to_monotonic / raw_sec │ └───────────┬───────────────────────────────┬───────────────┘ │ │ ┌────────┴────────┐ ┌────────┴────────┐ ▼ ▼ ▼ ▼ ktime_get() vsyscall.c hrtimer.c timer.c sched_clock() VDSO update 高精度定时器 timer wheel ntp.c 调整 用户态零 syscall nanosleep 等 mod_timer 等
|
数据流:硬件 counter → clocksource 读取消耗 → timekeeper 累加 → ktime_get* / VDSO;clockevent 中断 → tick → hrtimer 到期处理 +(必要时)jiffies 推进。
五、Clocksource 时钟源(clocksource.c)
5.1 核心概念
1 2 3 4 5 6 7 8
| struct clocksource { const char *name; u64 (*read)(struct clocksource *cs); u64 mask; u32 mult, shift; int rating; ... };
|
clocks_calc_mult_shift():计算 mult/shift,保证 maxsec 内不 64 位溢出
clocksource_register() / clocksource_select():按 rating 选择 curr_clocksource
- Watchdog:周期性比对两 clocksource,检测 TSC/arch_timer 漂移
5.2 时钟源层次(RK3588)
| 名称 |
rating |
来源 |
用途 |
arch_sys_counter |
450+ |
ARM Generic Timer |
主 clocksource |
jiffies |
1 |
jiffies.c |
兜底、boot 早期 |
驱动位于 drivers/clocksource/arm_arch_timer.c:
- CNTPCT(物理计数器)或 CNTVCT(虚拟计数器,KVM guest)
- 频率由 CNTFRQ 寄存器或 DT
clock-frequency 提供(通常 24 MHz)
- 同时注册 clocksource 与 per-CPU clockevent(PPI 中断)
- 支持 event stream(
CONFIG_ARM_ARCH_TIMER_EVTSTREAM)辅助 NO_HZ
六、Clockevents 时钟事件(clockevents.c)
6.1 设备状态机
1 2 3 4 5 6 7
| enum clock_event_state { CLOCK_EVT_STATE_DETACHED, CLOCK_EVT_STATE_SHUTDOWN, CLOCK_EVT_STATE_PERIODIC, CLOCK_EVT_STATE_ONESHOT, CLOCK_EVT_STATE_ONESHOT_STOPPED, };
|
clockevents_register_device():注册到 per-CPU tick_cpu_device
clockevents_switch_state():在 periodic ↔ oneshot 间切换
clockevent_delta2ns():设备 tick 数 → 纳秒
6.2 与 Tick 的关系
每个 CPU 有一个 struct tick_device(tick-common.c),绑定本 CPU 的 clockevent。Tick 处理函数 tick_handle_periodic 或 tick_handle_oneshot 由 tick-common.c / tick-sched.c 注册。
七、Timekeeping 时间维护(timekeeping.c)
7.1 核心结构
1 2 3 4 5 6 7 8 9
| static struct { seqcount_raw_spinlock_t seq; struct timekeeper timekeeper; } tk_core;
struct tk_fast { seqcount_latch_t seq; struct tk_read_base base[2]; };
|
struct timekeeper 包含:
xtime_sec / tkr_mono.xtime_nsec — CLOCK_REALTIME
wall_to_monotonic — realtime ↔ monotonic 偏移
raw_sec / tkr_raw — CLOCK_MONOTONIC_RAW(不受 NTP 调整)
tkr_mono / tkr_raw — 关联 clocksource 及 cycle_last
7.2 时间更新路径
- Tick 路径:
update_wall_time() — 每个 tick(或 NO_HZ 补偿多个 tick)读 clocksource delta,累加 xtime
- Fast path:
ktime_get() / ktime_get_mono_fast() — 读 tk_fast_mono,无锁 latch 序列
- Settime:
timekeeping_set_tk_clock() / do_settimeofday64() — 持 timekeeper_lock 写 shadow copy 后 swap
7.3 关键 API
| 函数 |
时钟类型 |
ktime_get() |
CLOCK_MONOTONIC |
ktime_get_real() |
CLOCK_REALTIME |
ktime_get_boottime() |
CLOCK_BOOTTIME |
ktime_get_raw() |
CLOCK_MONOTONIC_RAW |
ktime_get_coarse() |
粗粒度 monotonic(仅读 xtime_sec) |
八、Jiffies 与低精度定时器(jiffies.c / timer.c)
8.1 Jiffies
1
| __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
|
- 全局 tick 计数,精度 =
1/HZ 秒
jiffies_lock + jiffies_seq 保护 32 位架构上的 64 位读
clocksource_jiffies:rating=1 的兜底 clocksource
8.2 Timer Wheel(timer.c)
Linux 6.1 使用 多级 timer wheel(非 classic 级联 wheel):
1 2 3 4
| #define LVL_CLK_SHIFT 3 #define LVL_CLK_DIV 8 #define LVL_DEPTH 8 (或 9,64 位) #define LVL_SIZE 64
|
| 层级 |
粒度(HZ=250 时约) |
覆盖范围 |
| Level 0 |
4 ms |
短超时 |
| Level 1 |
32 ms |
|
| … |
每级 ×8 |
|
| Level 7/8 |
~18 天 |
最大超时 |
mod_timer() / add_timer() — 内核低精度定时器
- 基于
struct timer_list,在 run_timer_softirq 中处理
- 适合网络/块 I/O 超时等 大量可能被 cancel 的 timer
九、高精度定时器 hrtimer(hrtimer.c)
9.1 Per-CPU 基础结构
1
| DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
|
每个 CPU 有 8 个 clock base(hard + soft):
| Base |
Clock ID |
说明 |
| MONOTONIC |
CLOCK_MONOTONIC |
常用 |
| REALTIME |
CLOCK_REALTIME |
|
| BOOTTIME |
CLOCK_BOOTTIME |
含 suspend 时间 |
| TAI |
CLOCK_TAI |
|
| *_SOFT |
同上 |
在 softirq 上下文执行 callback |
定时器按 expiry 插入 红黑树;最近 expiry 编程到 clockevent oneshot。
9.2 核心 API
1 2 3
| hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_start(&timer, ns, HRTIMER_MODE_REL); hrtimer_cancel(&timer);
|
schedule_hrtimeout() — 可睡眠的精确延时
nanosleep / clock_nanosleep syscall 底层
hrtimer_interrupt — tick 或 clockevent 触发,处理到期 timer
9.3 Softtimer
HRTIMER_BASE_*_SOFT 的 callback 在 HRTIMER_SOFTIRQ 执行,允许 callback 中使用 mutex 等可能睡眠的操作(仍不可真正 schedule)。
十、Tick 子系统(tick-*.c)
10.1 tick-common.c
tick_handle_periodic / tick_do_timer — 全局 jiffies 推进(仅一个 CPU 负责 do_timer(),tick_do_timer_cpu)
tick_setup_periodic — 初始化 periodic tick
tick_program_event — 编程 oneshot 下次事件
10.2 tick-sched.c(NO_HZ / HIGH_RES_TIMERS)
1
| static DEFINE_PER_CPU(struct tick_sched, tick_cpu_sched);
|
- NO_HZ idle:CPU idle 时停止 tick;唤醒时
tick_do_update_jiffies64() 补偿多个 jiffies
- NO_HZ full:运行用户态任务时也停 tick(需
nohz_full= 启动参数)
tick_nohz_idle_stop_tick() / tick_nohz_idle_restart_tick()
- 与 RCU、scheduler loadavg、cputime Accounting 协同
10.3 tick-broadcast.c
当 CPU 进入 C3stop(arch timer 在 deep idle 停止)时,local clockevent 不可用,由 broadcast 设备 代发 tick。RK3588 big.LITTLE 中 A55 深 idle 常走此路径。
10.4 tick-oneshot.c
Clockevent 切换到 oneshot 模式的辅助;hrtimer 编程 next event 时使用。
十一、sched_clock 调度时钟(sched_clock.c)
1 2 3 4 5 6
| unsigned long long notrace sched_clock(void) { cyc = read_sched_clock() - epoch_cyc; return epoch_ns + cyc_to_ns(cyc, mult, shift); }
|
- 提供 单调 ns 计数,供 scheduler、tracing、perf 使用
- 早期用 jiffies;
sched_clock_register() 后切换为 arch_timer
sched_clock_tick() — 定期 extend 64 位范围,防 counter 回绕
- 与 timekeeping 独立:sched_clock 偏性能(可 slightly 漂移),wall clock 偏准确
十二、NTP 时间调整(ntp.c)
实现 adjtimex(2) / NTP PLL:
| 变量 |
含义 |
time_offset |
相位偏移(ns) |
time_freq |
频率偏移(scaled ppm) |
time_constant |
PLL 时间常数 |
time_status |
STA_PLL / STA_UNSYNC 等标志 |
tick_length |
每 tick 实际纳秒长度(含调整) |
ntp_update_frequency() — 应用频率校正
second_overflow() — 每秒边界处理 leap second
- 与
timekeeping.c 的 tk_set_xtime / timekeeping_adjust 协同
- RK3588 嵌入式场景通常 不跑 ntpd,但
adjtimex 仍可用于手动校时
十三、系统调用与用户接口(time.c)
| Syscall |
功能 |
gettimeofday / clock_gettime |
读时间(优先 VDSO) |
settimeofday / clock_settime |
设置系统时间 |
adjtimex |
NTP 参数调整 |
clock_getres |
时钟分辨率 |
times |
进程时间统计 |
sys_tz 导出时区;实际 wall clock 以 UTC 存储,用户态 libc 转换时区。
十四、POSIX 定时器(posix-timers.c)
timer_create / timer_settime / timer_gettime / timer_delete
clock_nanosleep — 线程级 nanosleep
- 基于 hrtimer 或 timerqueue 实现
- 哈希表管理 per-process timer ID(512 桶)
posix-cpu-timers.c — CLOCK_PROCESS_CPUTIME_ID / setitimer CPU 时间限制
itimer.c — 传统 interval timer(SIGALRM 等)
十五、Alarmtimer 与 RTC 唤醒(alarmtimer.c)
- 类似 hrtimer,但 suspend 时可借助 RTC 硬件唤醒
- Android 风格
alarm 接口;支持 CLOCK_REALTIME_ALARM / CLOCK_BOOTTIME_ALARM
- 与
drivers/rtc/ 协作,系统 suspend 前设置 RTC alarm
- RK3588 上 RTC 通常为 RK808/RK809 等 PMIC 内 RTC
十六、VDSO 时间页(vsyscall.c)
ARM64 通用 VDSO 更新(CONFIG_HAVE_GENERIC_VDSO):
1 2 3 4 5 6 7 8 9
| void update_vsyscall(struct timekeeper *tk) { vdata[CS_HRES_COARSE].cycle_last = tk->tkr_mono.cycle_last; vdata[CS_HRES_COARSE].mult = tk->tkr_mono.mult; vdso_write_begin(vdata); ... vdso_write_end(vdata); }
|
- 用户态
clock_gettime() 直接读 shared page(vdso_data),零 syscall
CS_HRES_COARSE:高精度 coarse 模式;CS_RAW:MONOTONIC_RAW
- timekeeper 更新时调用
update_vsyscall();seqlock 保证读者一致性
- ARM64 VDSO 使用 arch timer 模式(
VDSO_CLOCKMODE_ARCHTIMER)
十七、辅助模块
| 文件 |
作用 |
timecounter.c |
通用 timecounter/cyclecounter 辅助(驱动常用) |
timeconv.c |
时间单位转换 |
timer_list.c |
/proc/timer_list 调试接口 |
namespace.c |
时间命名空间(CONFIG_TIME_NS) |
posix-clock.c |
动态 POSIX clock 设备 |
timekeeping_debug.c |
debugfs 时间统计 |
clocksource-wdtest.c |
Watchdog 测试 |
test_udelay.c |
udelay 校准测试 |
十八、RK3588/ARM64 平台说明
18.1 硬件时间源
| 组件 |
说明 |
| ARM Generic Timer |
每核 CNTPCT @ CNTFRQ(通常 24 MHz);PPI 27/30 定时中断 |
| Architected Timer |
系统 counter ≥56 bit,40 年不回绕 |
| RK PMU Timer |
平台特定,非主 timekeeping |
| RTC |
PMIC RTC,仅 wall clock 备份与 wakealarm |
驱动:drivers/clocksource/arm_arch_timer.c
DT binding:Documentation/devicetree/bindings/timer/arm,arch_timer.yaml
18.2 配置总结
| 项目 |
RK3588 典型值 |
| NO_HZ |
y(tickless idle) |
| HIGH_RES_TIMERS |
y |
| HZ |
通常 100/250/300(由 CONFIG_HZ 决定) |
| Clocksource |
arch_sys_counter |
| Clockevent |
arch_timer per-CPU |
| Broadcast |
启用(deep idle A55 核) |
| VDSO |
启用(clock_gettime 用户态 fast path) |
| sched_clock |
arch_timer 扩展 |
18.3 big.LITTLE 注意点
- Per-CPU tick:每核独立 clockevent 与 hrtimer base;NO_HZ 各核独立停/启 tick
- Broadcast tick:little 核深 idle 时 timer 停,需 broadcast 维持 RCU/sched deadline
- Timekeeping CPU:
tick_do_timer_cpu 指定单一 CPU 推进 global jiffies,避免 thundering herd
- CPU hotplug:核下线/上线时 tick 设备迁移,
timekeeping 不变
- Virtual counter:KVM guest 使用 CNTVCT;host 用 CNTPCT
18.4 与调度/电源的交叉
- CFS 调度:
sched_clock() 计算 vruntime
- NO_HZ idle:与
cpuidle 配合,A55 核 idle 停 tick 省电
- Hrtimer:multimedia pipeline、音频(ALSA)等依赖高精度超时
- Printk timestamp:可选
PRINTK_TIME_FROM_ARM_ARCH_TIMER 直接用 arch timer
十九、完整 Tick 中断时序
二十、总结
| 要点 |
说明 |
| 双抽象 |
clocksource 读时间,clockevent 产生事件 |
| timekeeping |
单一 timekeeper + fast latch,NMI 安全 |
| 双定时器 |
hrtimer(高精度 RB-tree)+ timer wheel(低精度海量 timeout) |
| NO_HZ |
idle 停 tick 省电,唤醒补偿 jiffies |
| VDSO |
用户态无 syscall 读时间 |
| RK3588 |
ARM arch timer + tickless + hrtimer;8 核 broadcast 支持 deep idle |
kernel/time/ 是调度、RCU tick、POSIX、tracing、media 等子系统的时间基础;性能与功耗调优常涉及 NO_HZ、hrtimer 密度与 broadcast 行为。
附录:源文件清单
| 文件 |
行数 |
说明 |
timekeeping.c |
2503 |
时间维护核心 |
hrtimer.c |
2394 |
高精度定时器 |
timer.c |
2166 |
Timer wheel |
posix-cpu-timers.c |
1693 |
CPU 时间定时器 |
tick-sched.c |
1626 |
NO_HZ tick 调度 |
clocksource.c |
1519 |
时钟源管理 |
posix-timers.c |
1458 |
POSIX 定时器 |
tick-broadcast.c |
1235 |
Broadcast tick |
ntp.c |
1095 |
NTP 调整 |
alarmtimer.c |
964 |
Alarm 定时器 |
time.c |
909 |
系统调用 |
clockevents.c |
778 |
时钟事件 |
tick-common.c |
564 |
Tick 公共逻辑 |
namespace.c |
467 |
时间命名空间 |
itimer.c |
403 |
Interval timer |
timer_list.c |
360 |
/proc 调试 |
posix-clock.c |
320 |
动态 POSIX clock |
sched_clock.c |
296 |
sched_clock |
posix-stubs.c |
254 |
无 POSIX 时的 stub |
clocksource-wdtest.c |
202 |
Watchdog 测试 |
tick-internal.h |
199 |
Tick 内部头文件 |
vsyscall.c |
170 |
VDSO 更新 |
test_udelay.c |
159 |
udelay 测试 |
timeconv.c |
141 |
时间转换 |
tick-oneshot.c |
128 |
Oneshot tick |
tick-broadcast-hrtimer.c |
111 |
Broadcast hrtimer |
jiffies.c |
104 |
Jiffies clocksource |
time_test.c |
99 |
KUnit 测试 |
timecounter.c |
99 |
timecounter 辅助 |
timekeeping_debug.c |
55 |
debugfs |
posix-timers.h |
45 |
POSIX 内部头 |
timekeeping_internal.h |
39 |
timekeeping 内部 |
tick-legacy.c |
37 |
遗留 tick |
timekeeping.h |
34 |
timekeeping 头 |
ntp_internal.h |
22 |
NTP 内部 |
| 合计 |
22753 |
|
行数统计:Linux 6.1,wc -l kernel/time/*.{c,h}。
正在加载留言…