Linux时间子系统学习笔记

时间子系统

概述: 主要负责时间管理、时钟源选择、计时器事件处理、时间同步等核心功能。

时钟源

简介

概述: 是硬件计时器,用于提供高精度、连续不断的计数值,常用的时钟源包括TSC(跟cpu频率相关,且通过hpet等其他时钟源校准tsc频率)、HPET、ACPI定时器等。内核在启动过程中会探测可用的时钟源,并选择精度高、稳定性好的作为系统的主要时间计数器。

作用: 内核利用时钟源的计数值计算经过的时间,通过将计数器的变化量转换为实际时间(通常通过除以配置的时钟频率CONFIG_HZ),从而维护系统时间和其他时间相关功能。

物理时钟源:

  • rtc:独立于CPU的实时时钟,通常用于保存系统时间。由电池供电,即使在系统断电时也能持续运行。系统会在启动时从RTC读取当前时间,作为系统时间的初始值,并在关机或定期校正时将系统时间写回RTC。
  • tsc:x86 CPU内置的时间戳计数器,精度高但可能不稳定(多核同步问题)
  • hpet:高精度定时器
  • acpi pm timer:用于acpi电源管理,精度低但稳定

虚拟时钟源:

  • kvm-clock:KVM提供的高效时钟源,适用于KVM/QEMU虚拟机,减少VM-Exit,提高精度
  • xen-clocksource:Xen虚拟机环境下的时钟源
  • hypervclock:Microsoft Hyper-V提供的时钟源

时钟源选择:

  • 通过/sys/devices/system/clocksource/clocksource0/current_clocksource选择时钟源
  • cat /proc/uptime也可以间接反映时钟源的精度

TSC

概述: 现代cpu通常支持invariant tsc,即TSC的计数速率固定,不受CPU频率变化影响,如果硬件满足这一条件,linux内核通常会优先选择TSC作为主时钟源,因为其开销低且读取迅速

怎么读取tsc值:

  • 直接使用汇编指令rdtsc :tsc是一个64位的寄存器,自系统启动后开始计数,每个时钟周期递增。rdtsc的作用就是将这个计数器的当前值读取出来。
  • 内核态中可以使用tsc_read_refs函数:这个在上面汇编指令的基础上进一步做一个高级抽象的时钟源,原因:
    • linux内核不仅可能使用tsc,也可能使用hpet等其他时钟源,这样做抽象方便管理
    • 校准与转换:直接读取返回的是时钟周期数,要需要进一步进行转换为标准的时间单位(如纳秒),以便在调度、定时器和用户空间应用中使用一致的时间格式
    • 保证单调性与跨核一致性:多核系统中,各个核心tsc可能不同步或存在细微差别,在tsc抽象实现中会采取相应措施保证单调性和一致性(比如rdtsc指令本身是非序列化的,可以配合其他指令(CPUID或RDTSCP)确保时间戳读取的顺序)
  • 用户态可以使用clock_gettime系统调用:linux内核一般会选tsc

稳定性检测

频率漂移检测:

  • 内核会定期测量不同时钟源的频率,并对比它们之间的偏差
  • 例如,TSC可能会受到CPU频率调节影响,因此会与HPET、ACPI PM Timer等参考时钟进行对比
  • 若发现某个时钟源的频率偏差超出允许范围,则可能会被降级或禁用

单调性检查: 内核会确保时钟源的时间值是单调递增的,不能出现回退现象

时间跳变检测: 通过定期对比系统时间与参考时钟(如NTP服务器),检测是否出现了异常跳变

多核一致性检测: 对于TSC,linux会检测多个CPU核心的TSC是否同步,以防止不同核心返回的TSC不一致

定期重新校准: linux可能会周期性地使用hpet活外部时钟(NTP)重新校准TSC

kvm-clock

概述: KVM提供的一个半虚拟化时钟源,其核心目的是为guest os提供一个高效、低开销、尽可能准确的计时机制。避免传统硬件时钟(如TSC、HPET)在虚拟化场景中可能遇到的同步、漂移或性能问题。【本质上其实也依赖于TSC吧,只是依赖的host os的tsc,可能误差更小】

工作原理:

  • 共享内存区域:
    • Guest OS在初始化时会为kvm-clock分配一段共享内存,这块内存区域是由hypervisor(例如qemu/kvm)映射给guest
    • 该区域存储一个数据结构(pvclock_vcpu_time_info),其中包含当前的tsc值,系统时间、以及一些转换参数(如tsc_to_system_multsc_shift
    • Guest通过读取这个共享区域,就可以直接获得当前时间,而不需要进行昂贵的系统调用或VM Exit
  • 主机与客机时间同步:
    • Hypervisor定期更新这块共享内存区域,确保guest看到的时间与host系统时间保持一致
    • 不仅降低了guest os对硬件时钟的依赖,而且可以减少虚拟化带来的时间漂移问题
  • 效率与精度:
    • 由于 kvm-clock 利用共享内存而非每次调用硬件寄存器(如 TSC),因此能够避免频繁的虚拟机退出(VM Exit),降低延迟。
    • 同时,通过对 TSC 值进行适当转换,kvm-clock 能够提供一个相对稳定、单调递增的时钟源,即使在 CPU 频率变动或多核环境下也能维持较高的精度。

实现细节:

  • 数据结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct pvclock_vcpu_time_info {
    u32 version;
    u32 pad0;
    u64 tsc_timestamp;
    u64 system_time;
    u32 tsc_to_system_mul;
    s8 tsc_shift;
    u8 flags;
    u8 pad[2];
    } __attribute__((__packed__));
    • version:用于保证读取时的一致性(类似双缓冲机制)
    • tsc_timestamp:记录了上次更新时间时的 TSC 值。
    • system_time:对应的系统时间(通常以纳秒为单位)
    • tsc_to_system_mul和tsc_shift:用于将 TSC 增量转换为系统时间的比例因子
  • 更新策略:Hypervisor 并不会在每次时间查询时都更新这块区域,而是在关键事件(如 VM 启动、迁移、或其他时间敏感操作后)进行更新,以减少性能开销。

优缺点:

  • 优点:

    • 低开销:共享内存的访问非常快,避免了 VM Exit
    • 高效性:减少了依赖传统硬件时钟带来的额外延迟
    • 单调性:提供一个单调递增的时间源,对时间敏感型应用(例如网络协议、会话管理)非常关键
  • 缺点:

    • 依赖host os更新:如果 hypervisor 更新不及时,guest 可能会遇到时间漂移问题。

    • 特殊场景问题:在某些情况下(如热添加 CPU 等操作),kvm-clock 可能会暴露出时间回退或不一致的问题,需要内核补丁或额外配置加以解决

      新加入的vCPU并不会立即与已有的vCPU同步它们的kvm-clock状态

时钟事件

简介

功能: 时钟事件设备负责生成定时中断,用以触发内核中断各类定时任务,例如,任务调度、定时器超时处理以及高精度定时器的触发都依赖于时钟事件设备(比如APIC定时器设备)

抽象接口: linux为了兼容不同硬件平台,定义了统一的时钟事件接口,这样无论底层硬件如何差异化,内核上层的定时器管理和调度代码都能以相同的方式调用,极大提高代码的可移植性和维护性

计时器

周期性计时器:

  • jiffies:是一个全局计时变量,用来记录自系统启动以来经过的时钟tick数,虽然jiffies本身不是以秒为单位的,但通过与时钟频率(HZ)的结合,可以方便地将其转换为秒数或其他时间单位。本身单位是ticks,在内核中通过HZ来定义,一个tick通常是100或1000,这意味着每秒会有100或1000次jiffies更新,可以说是0.1ms或1ms(而HPET和ACPI PM Timer的单位为ns,可见精度更高)
  • tickless(NO_HZ模式):允许系统在空闲时关闭定时器,降低功耗。系统在空闲状态下不再产生周期性中断,而是根据定时器的到期时间动态安排中断,从而减少不必要的中断开销,并改善系统在低功耗状态下的表现。

高精度计时器:

  • 提供纳秒级精度,例如hpet等

定时器中断:

  • 由硬件触发,如APIC timer、PIT(可编程间隔定时器)

时间管理

系统时间

墙上时间: 指的是系统的“当前时间”,通常由CLOCK_REALTIME表示。反映了真实世界的时间,这个时间可以通过系统管理员手动设置、从硬件实时时钟(RTC)读取,或者通过网络时间协议(NTP)以及HPET等进行矫正

单调时间: 是一个只会不断递增、不受外部时间调整影响的时间计数。通常由CLOCK_MONOTONIC提供,主要特点:

  • 始终递增:无论系统时间如何调整,它总是保证不会倒退
  • 适合测量时间间隔:用于计算事件之间的持续时间、超时或调度,而不必担心因墙上时间被修改而导致错误

二者区别:

  • 使用场景:
    • 墙上时间:适合需要显示或记录“真实”时间的应用,如日志记录、文件时间戳、用户界面显示等
    • 单调时间:适合需要精确测量事件间隔的长i纪念馆,如性能测试、任务调度、超时管理等
  • 底层实现:
    • 墙上时间:依赖于硬件时钟(RTC)以及系统软件对时间的同步和矫正机制
    • 单调时间:依赖于系统内部的高精度计时器(如TSC、HPET或其他硬件时钟),并经过内核抽象层保证其单调性和一致性

时钟同步

NTP与时间平滑调整: 系统时间可能会因网络时间同步(NTP)而进行调整,为了防止时间突然跳变(如突然回调或前移),内核采用slewing算法来平滑调整系统时间,使得时间校正过程对应用层的影响降到最低

多处理器同步: 在SMP系统中,确保各个CPU核心时间一致性是非常关键的。内核通过同步机制使得所有核心共享同一时间基准,避免因各核心时间不同步而引发调度和资源竞争问题

Watchdog

目的: 确保系统使用的时钟源稳定且精确。因为时钟源直接影响到系统时间的计算和更新,所以必须排除那些在短时间内发生大幅偏差的不可靠时钟源

背景: 在系统启动过程中,内核会注册多个clocksource,每个clocksource都会被赋予一个rating,表示它的精度和稳定性。为了避免由于硬件故障或环境因素导致某个时钟源计数异常,内核引入了watchdog机制来动态监控各个clocksource的表现

实现原理:

  1. 周期性检测:watchdog会定期检查所有支持watchdog功能的clocksource。他会读取当前的周期计数值,并与前一次检查时保存的值进行比较
  2. 预期增量计算:根据clocksource的已知频率、mult和shift参数,内核可以将计数器的增量转换为经过的时间,此时就有一个“预期”的时间增量值
  3. 偏差比较:如果实际读出的增量与预期值之间的差异超过了预定义的阈值,则认为该时钟源存在问题。
  4. 动态降级与选择:在检测到偏差过大时,watchdog会降低clocksource的rating,从而防止它被选为当前系统的主要时钟源。然后watchdog会选择rating更高的时钟源作为系统时间的基础

简单来说,所有支持watchdog的clocksource都会被加入一个list,内核利用定时器或专用内核线程定期遍历该列表


Linux时间子系统学习笔记
http://example.com/2025/03/06/Linux时间子系统学习笔记/
作者
凌云行者
发布于
2025年3月6日
许可协议