Linux内核时钟与定时器的实现
一、概述
在计算机系统,CPU是以一个节拍一个节拍运行的(cpu cycle),这就是CPU的频率(HZ)。类似的,操作系统需要提供超时功能,显示时间(如PC机右下角的时钟),统计(CPU占有率计算)等功能,也需要有一个节拍(操作系统的频率HZ)触发操作系统做上述相关功能处理,内核在内部使用了一个全局变量记录了系统从启动后经过了多少个节拍,这就是tick值,通常由另外一个更形象的说法“嘀嗒”,在linux下用jiffies表示(该值为long型,为了防止32位系统下该值翻转,同时用了一个64位数jiffies_64来记录)。
那么谁来触发这个节拍呢,通常由时钟芯片来实现。由于内核需要运行在不同的硬件设备上,因此,内核抽象出了通用的时钟事件处理框架。其典型流程如下:
硬件<-->相关驱动代码<-->通用时钟处理代码<-->高分辨率定时器处理框架<-->高/低分辨率定时器处理<-->超时处理
上述流程从左到右为时钟触发处理流程,从右到左是设置配置流程。高分辨率定时器提供了比低分辨率定时器高很多的精度(通常前者为纳秒级,而后者为毫秒级),高精度定时器需要在编译内核时指定CONFIG_HIGH_RES_TIMERS才支持。
二、实现文件及入口函数说明:
clockchips.h/clockevents.c: 定义时钟事件设备struct clock_event_device,该结构抽象了时钟事件的通用处理代码,从结构体定义可以看出,主要为设置时钟芯片的触发模式(周期触发还是单触发),设置超时事件,已经超时事件的处理函数(时钟中断处理)等。
clocksource.h/clocksource.c: 定义了时钟源struct clocksource,该结构抽象了时钟源的处理,主要是使能时钟源,去使能时钟源,读取时钟源的cycle值,暂停和恢复时钟源等操作。
这4个文件为通用时钟处理代码,在它们的下一层,就是时钟芯片的驱动代码。如IA-32/AMD CPU的PIT(programmable interrupt timer,由时钟芯片8253实现,相关实现可参看I8253.h/I8253.c
tick.h: tick时钟头文件,定义了两个结构:
struct tick_device:经典tick时钟设备定义,低精度定时器。
struct tick_sched: 高精度定时器模拟tick时钟,或动态时钟(tickless)使用。
tick-common.c:经典tick时钟的实现。tick_handle_periodic为入口函数
tick_handle_periodic->do_timer(全局时钟)->jiffies_64更新
update_wall_time
calc_global_load
->update_process_times->run_local_timers->hrtimer_run_queues/TIMER_SOFTIRQ->__run_timers(低分辨率定时器)
rcu_check_callbacks
scheduler_tick
run_posix_cpu_timers
tick-internal.h:一些通用的支撑代码
tick-broadcast.c:广播模式的时钟处理。在一些系统,为了省电进入节能模式后,可能会停止某些时钟事件,这时候可以通过其他的设备广播一个时钟事件。
tick-oneshot.c:高精度定时器单触发方式的一些通用处理代码
tick-sched.c: 切换到动态时钟及动态时钟的实现
timer.c/h:低精度定时器的实现, 定时器节点管理上采用了级联方式。
hrtimer.c/h:高精度定时器的实现。hrtimer_interrupt为入口函数
hrtimer_interrupt->__run_hrtimer
与hrtimer_run_queues的区别?是通用框架的一部分,在每个硬件中断中触发。called by run_local_timers,没有高精度时钟系统才会运行。
timerqueue.c/h:高精度定时器节点队列,封装了rbtree的操作。
ktime_t为高精度定时器的定时时间
tick_setup_sched_timer:在切换到高精度模式时,启动一个高精度定时器模拟周期tick时钟。
struct hrtimer_sleeper :任务定时操作是如此普遍,内核实现了此类公用接口,以方便使用。