介绍
Linux 操作系统在现代计算机系统中扮演着重要的角色,其核心功能之一是准确可靠的时间管理。时钟系统在操作系统中用于管理时间戳、调度任务和同步分布式系统等方面。在 Linux 中,时钟管理机制设计复杂,包含了多种类型的时钟、定时器和硬件接口,确保系统能够精确、稳定地维护和调整时间。本文将深入剖析 Linux 内核时钟系统的各个方面,包括其内部机制、常见类型、同步方式以及如何在 Linux 应用程序中正确使用。
Linux 时钟的基本概念
Linux 操作系统中包含多个概念不同的时钟,包括系统时钟、硬件时钟和高精度时钟等。
系统时钟 (System Clock):指操作系统维护的当前时间,它是用户态应用程序获取时间的主要来源。系统时钟的计时通常基于 CPU 时钟频率。
硬件时钟 (Hardware Clock):也称为实时时钟 (RTC, Real-Time Clock),是主板上独立的计时器,通常包含一块备用电池,可以在关机时继续计时。硬件时钟用于在系统启动时设置系统时间。
高精度时钟 (High-Resolution Timers):一些应用(如多媒体和实时应用)需要高精度的计时。Linux 提供了 hrtimers(高精度定时器)来支持毫秒级、甚至更高精度的计时。
定时器中断(Timer Interrupt):内核通常使用定时器中断来跟踪时间间隔(通常用于服务应用程序调用的各种定时器),定时器中断由系统的定时器硬件在预定义的周期内生成,由变量HZ设置。此变量定义在 根据 HZ 的值(可以在内核构建时设置),系统支持的基本最小计时器值(基本时钟中断频率)可以改变(默认为 10 毫秒)。
时钟滴答(jiffies):时钟滴答是内核维护的一个内部系统变量,用于存储自系统开机(启动)以来的时钟滴答数。它在 <linux/schedule.h> 中定义。
内核定时器(kernel timers):定时器是一种确保任何模块中时间关键功能的机制。对于任务需要检查缓冲区或需要在特定时间段(无论是周期性还是离散性)后轮询任何设备的情况,必须有一种机制在特定间隔结束时通知任务,以便任务可以自由地完成其正常工作过程,而无需打扰或等待通知的到来。计时器过程用于满足任何应用程序中的此类需求。
内核定时器以双向链接列表的形式组织,其中前向指针和后向指针均在运行。定时器的特征在于其到期值、标识标签(名称、所属进程和其他数据)以及定时器到期时调用的函数(处理程序)。定时器的数据结构如下所示(#<linux/timer.h>)
定时器列表结构
{
struct timer_list *next; //指向列表中下一个定时器的指针
struct timer_list *prev; //指向列表中前一个定时器的指针
unsigned long expires; // jiffies 中的超时值
无符号长整型数据;//处理程序的参数
void (*function) (unsigned long); //处理函数
volatile int running; //表示节点状态的标志
};
当timer->expires>=jiffies时,将调用定时器处理程序。一旦timer_list结构初始化,add_timer()就会将其插入到定期轮询的排序列表中。下面给出了所有内核定时器 API:
void init_timer (struct timer_list* timer); 此函数初始化新分配的定时器。
void add_timer (struct timer_list* timer); 此函数将新分配的计时器插入到活动计时器的全局列表中。
void mod_timer (struct timer_list* timer, unsigned long expires); 此函数用于修改活动计时器的值。
int del_timer (struct timer_list* timer); 该函数从活动定时器列表中删除指定的定时器。
Linux内核的时钟管理机制
Linux 内核的时间管理机制由时钟源(Clock Source)、时钟事件(Clock Event)和时间保持(Timekeeping)三个子系统组成。
时钟源 (Clock Source):Linux 支持多种时钟源来记录时间,包括 TSC(时间戳计数器)、HPET(高精度事件定时器)和 ACPI PM(高级配置与电源管理)。系统在启动时选择最适合的时钟源,以确保精确性和性能。
时钟事件 (Clock Event):负责处理时钟中断的模块。内核中的时钟事件驱动可基于硬件定时器,如 APIC 定时器和 HPET,来产生周期性中断,用于触发定时任务。
时间保持 (Timekeeping):内核通过 Timekeeping 模块来维护系统时钟。该模块的主要任务是记录经过的时间、管理系统时间,并确保多核系统中各处理器间的时间一致性。
图1 Linux内核时间保持机制原理
Linux内核时间保持机制在kernel/time/timekeeping.c中实现。在timekeeping.c中,为了减轻操作系统的运算开销,以及抵抗不可屏蔽中断等的干扰,Linux采用了序列计数的方法保护时钟变量的更新,例如:在函数ktime_get_real_ts64中的序列计数保护:
823 do {
824 seq = read_seqcount_begin(&tk_core.seq);
825
826 ts->tv_sec = tk->xtime_sec;
827 nsecs = timekeeping_get_ns(&tk->tkr_mono);
828
829 } while (read_seqcount_retry(&tk_core.seq, seq));
第829行会比较之前取得的seq值和tk_core.seq,如果不同,则表明更新者对时钟变量进行了更新,那么就需要重新进行读取。
高精度定时器(High-Resolution Timers,hrtimers)是 Linux 内核提供的一种高精度计时机制。传统的 jiffies 定时器只能提供低于系统时钟频率的精度,而 hrtimers 则能提供亚毫秒级的精度,因此在实时系统和高频交易等领域有广泛应用。
图2 Linux高精度时钟实现原理
图2给出了Linux高精度时钟实现原理:Linux系统在当前高精度时钟到期后,首先进行回调,然后计算最近一个到期的时钟(在红黑树中维护)的到期时间,并以此对时钟源进行设置,如此不断进行下去。
总结
Linux内核的时钟系统是一个复杂的结构,由多个组件协同工作,以实现精确的时间管理和调度。时钟源(Clock Source)用于提供当前系统时间的基础。每个时钟源会根据硬件计时器的频率提供一个时间基准,常见的时钟源有TSC(时间戳计数器)、HPET(高精度事件计时器)等。系统根据不同的精确性和需求选择适合的时钟源。时钟事件(Clock Events)驱动系统定时任务,例如内核调度、定时中断等。时钟事件是通过硬件计时器(如APIC、PIT等)触发的,能够以周期性或单次模式向内核发送中断信号,用于执行调度任务或唤醒进程。时钟(Clock)负责周期性产生系统心跳(jiffies),这些心跳用来触发系统的定时事件。定时器(Timer)主要用于用户态和内核态的时间延迟请求,基于时钟的心跳,在指定的时间后执行特定的任务。时间管理(Timekeeping)负责记录和跟踪系统的当前时间,并提供面向用户的时间表示,例如系统启动时间、当前时间、定时器延迟等。timekeeping模块结合了时钟源的数据,并根据不同的校正机制(如NTP)保持时间的精确性。传统的Linux内核使用固定频率的周期性时钟中断,称为“tick-based”。高分辨率时钟(hrtimer)的引入使得内核可以在需要时才触发时钟中断(“tickless”模式),减少了CPU的中断负载,提高了低负载时的电源效率。
图3 Linux内核时钟系统各个组件之间的关系
这些组件相互协作,共同构成了Linux内核的时钟系统。时钟源提供精准的计时基础,时钟事件确保调度的准确性,定时器和时间管理系统负责时间跟踪和任务调度,而高分辨率和动态时钟则提高了系统在不同负载下的响应能力与功耗管理。