kernel/time 内核时间与定时器机制与原理详解

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; /* 硬件 tick → ns 的缩放因子 */
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 streamCONFIG_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, /* 周期性 HZ tick */
CLOCK_EVT_STATE_ONESHOT, /* 单次触发(hrtimer/NO_HZ) */
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_devicetick-common.c),绑定本 CPU 的 clockevent。Tick 处理函数 tick_handle_periodictick_handle_oneshottick-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 { /* NMI 安全快速路径 */
seqcount_latch_t seq;
struct tk_read_base base[2]; /* 双缓冲 latch */
};

struct timekeeper 包含:

  • xtime_sec / tkr_mono.xtime_nsecCLOCK_REALTIME
  • wall_to_monotonic — realtime ↔ monotonic 偏移
  • raw_sec / tkr_rawCLOCK_MONOTONIC_RAW(不受 NTP 调整)
  • tkr_mono / tkr_raw — 关联 clocksource 及 cycle_last

7.2 时间更新路径

  1. Tick 路径update_wall_time() — 每个 tick(或 NO_HZ 补偿多个 tick)读 clocksource delta,累加 xtime
  2. Fast pathktime_get() / ktime_get_mono_fast() — 读 tk_fast_mono,无锁 latch 序列
  3. Settimetimekeeping_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)
{
/* seqcount_latch 双缓冲读 */
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.ctk_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
  • 基于 hrtimertimerqueue 实现
  • 哈希表管理 per-process timer ID(512 桶)
  • posix-cpu-timers.cCLOCK_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;
/* CLOCK_MONOTONIC / BOOTTIME / TAI / REALTIME basetime */
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 CPUtick_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 中断时序

timer wheeltimekeepinghrtimertick_handlerclockeventARM Generic Timertimer wheeltimekeepinghrtimertick_handlerclockeventARM Generic TimerNO_HZ: 编程下次最近 event,可能跳过 tickPPI 中断 (oneshot/periodic)tick_handle_oneshot()hrtimer_interrupt()红黑树取出到期 timer,执行 callbackupdate_wall_time() (若需推进)clocksource delta → xtime_nsecrun_timer_softirq (via raise_softirq)clockevents_program_event(next)

二十、总结

要点 说明
双抽象 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}

文章互动

阅读 --

留言

0 条留言

正在加载留言…