当前位置:文档之家› 内核2.6.X中的时钟与定时器

内核2.6.X中的时钟与定时器

内核2.6.X中的时钟与定时器2007年07月11日星期三 15:47时钟和定时器对Linux内核来说十分重要。

首先内核要管理系统的运行时间(uptime)和当前墙上时间(wall time),即当前实际时间。

其次,内核中大量的活动由时间驱动(time driven)。

其中一些活动是周期性的,比如调度调度器(scheduler)中的运行队列(runqueue)或者刷新屏幕这样的活动,它们以固有的频率定时发生;同时,内核要非周期性地调度某些函数在未来某个时间发生,比如推迟执行的磁盘I/O操作等。

实时时钟--------------------------------------------------------- 内核必须借助硬件来实现时间管理。

实时时钟(real time clock)是用来持久存放系统时间的设备,它与CMOS集成在一起,并通过主板电池供电,所以即便在关闭计算机系统之后,实时时钟仍然能继续工作。

系统启动时,内核读取实时时钟,将所读的时间存放在变量xtime 中作为墙上时间(wall time),xtime保存着从1970年1月1日0:00到当前时刻所经历的秒数。

虽然在Intel x86机器上,内核会周期性地将当前时间存回实时时钟中,但应该明确,实时时钟的主要作用就是在启动时初始化墙上时间xtime。

系统定时器与动态定时器--------------------------------------------------------- 周期性发生的事件都是由系统定时器(system timer)驱动。

在X86体系结构上,系统定时器通常是一种可编程硬件芯片(如8254 CMOS芯片),又称可编程间隔定时器(PIT, Programmable Interval Timer),其产生的中断就是时钟中断(timer interrupt)。

时钟中断对应的处理程序负责更新系统时间和执行周期性运行的任务。

系统定时器的频率称为节拍率(tick rate),在内核中表示为HZ。

以X86为例,在2.4之前的内核中其大小为100;从内核2.6开始,HZ = 1000,也就是说每秒时钟中断发生1000次。

这一变化使得系统定时器的精度(resolution)由10ms提高到1ms,这大大提高了系统对于时间驱动事件调度的精确性。

过于频繁的时钟中断不可避免地增加了系统开销(overhead),但是总的来说,在现在计算机系统上,HZ = 1000不会导致难以接受的系统开销。

与系统定时器相对的是动态定时器(dynamic timer),它是调度事件(执行调度程序)在未来某个时刻发生的时机。

内核可以动态地创建或销毁动态定时器。

系统定时器及其中断处理程序是内核管理机制的中枢,下面是一些利用系统定时器周期执行的工作(中断处理程序所做的工作):(1) 更新系统运行时间(uptime)(2) 更新当前墙上时间(wall time)(3) 在对称多处理器系统(SMP)上,均衡调度各处理器上的运行队列(4) 检查当前进程是否用完了时间片(time slice),如果用尽,则进行重新调度(5) 运行超时的动态定时器(6) 更新资源耗尽和处理器时间的统计值内核动态定时器依赖于系统时钟中断,因为只有在系统时钟中断发生后内核才会去检查当前是否有超时的动态定时器。

X86体系结构中时钟资源还包括CPU本地APIC(local Advanced Programmable Interrupt Controller)中的定时器和时间戳计时器TSC(Time Stamp Counter)。

高精度定时器将使用CPU本地APIC作为高精度定时中断源。

高精度定时器的设计与实现---------------------------------------------------------X86体系结构中,内核2.6.X的HZ = 1000,即系统时钟中断执行粒度为1ms,这意味着系统中周期事情最快为1ms执行一次,而不可能有更高的精度。

动态定时器随时都可能超时,但由于只有在系统时钟中断到来时内核才会检查执行超时的动态定时器,所以动态定时器的平均误差大约为半个系统时钟周期(即0.5ms).对于实时要求较高的电信应用来说,普通Linux在实时性方面与电信平台的要求之间还存在一定的差距。

CGL为了增强Linux的软实时能力,在以下方面对内核进行了改进:提供高精度的动态定时器;提供可抢占式内核(preemption kernel)等。

下面主要介绍高精度实时器的设计思想及实现,该实现遵循PISIX 1003.1b中时钟和定时器相关API标准,方便应用程序开发人员的使用。

高精度定时器的基本设计思想为:用(jiffies+sub_jiffie)表示动态定时器的超时时间,PIT仍然按频率HZ = 1000产生系统时钟中断。

如果在一个时钟中断tick与下一个时钟中断(tick+1)之间,即[jiffies, jiffies+1)之间,有高精度动态定时器等待处理(超时时间表示为(jiffies+sub_jiffie), sub_jiffie < 1),那么用最近的动态定时器超时值sub_jiffie对硬件定时器(PIT或local APIC)进行设定,使其在时刻(jiffies+sub_jiffie)产生中断,通知内核对该高精度定时器进行处理。

而不必总是等到系统时钟中断到来后才检查执行所有超时的定时器,从而达到提高动态定时器精度的目的。

高精度定时器的中断源--------------------------------------------------------- 高精度定时器在内核中,仍然使用可编程间隔定时器PIC产生每秒HZ次的系统时钟中断,对于采用哪种硬件定时器产生高精度定时器中断则取决于CPU上是否有本地APIC。

若CPU上没有本地APIC,那么仍可使用PIT产生高精度定时中断,虽然这种然PIT即产生系统时钟中断又产生高精度定时器中断的做法效率不高。

获取高精度定时器的发生时间--------------------------------------------------------- 高精度定时器发生在连续两个jiffies之间(即时刻(jiffies+sub_jiffie)),要确定其产生时间,就必须确定sub_jiffie 的大小。

通过函数get_arch_cycles(ref_jiffies)可获取sub_jiffie 的值,sub_jiffe以CPU时钟周期为最小计时单位。

函数具体实现思想是,通过访问计数器TSC,计算从上一个jiffies到当前时刻ref_jiffies之间的TSC差值,最终确定sub_jiffies的大小。

The high-resolution timer API---------------------------------------------------------Last September, this page featured an article on the ktimers patch by Thomas Gleixner. The new timer abstraction was designedto enable the provision of high-resolution timers in the kernel and to address some of the inefficiencies encountered when the current timer code is used in this mode. Since then, there has been a large amount of discussion, and the code has seen significant work. The end product of that work, now called "hrtimers," was merged for the 2.6.16 release.At its core, the hrtimer mechanism remains the same. Rather than using the "timer wheel" data structure, hrtimers live on a time-sorted linked list, with the next timer to expire being at the head of the list. A separate red/black tree is also used to enable the insertion and removal of timer events without scanning through the list. But while the core remains the same, just about everything else has changed, at least superficially.struct ktime_t-----------------------------There is a new type, ktime_t, which is used to store a time value in nanoseconds. This type, found in <linux/ktime.h>, is meant to be used as an opaque structure. And, interestingly, its definition changes depending on the underlying architecture. On64-bit systems, a ktime_t is really just a 64-bit integer value in nanoseconds. On 32-bit machines, however, it is a two-field structure: one 32-bit value holds the number of seconds, and the other holds nanoseconds. The order of the two fields depends on whether the host architecture is big-endian or not; they are always arranged so that the two values can, when needed, be treated as a single, 64-bit value. Doing things this way complicates the header files, but it provides for efficient time value manipulation on all architectures.struct--ktime_ttypedef union {On 64-bit systems|----------------------|| s64 tv64; ||----------------------|On 32-bit machines|----------------------|| struct { || s32 sec, nsec; || s32 nsec, sec; || } tv; ||----------------------|} ktime_t;初始化ktime_t-----------------------------A whole set of functions and macros has been provided for working with ktime_t values, starting with the traditional two ways to declare and initialize them(ktime_t values):(1) Initialize to zeroDEFINE_KTIME(name);(2) ktime_t kt;kt = ktime_set(long secs, long nanosecs);初始化高精度时钟hrtimer-----------------------------The interface for hrtimers can be found in <linux/hrtimer.h>.A timer is represented by struct hrtimer, which must be initialized with:void hrtimer_init(struct hrtimer *timer, clockid_twhich_clock);System clocks-----------------------------Every hrtimer is bound to a specific clock. The system currently supports two clocks, being:* CLOCK_MONOTONIC: a clock which is guaranteed always to move forward in time, but which does not reflect "wall clock time" in any specific way. In the current implementation, CLOCK_MONOTONIC resembles the jiffies tick count in that it starts at zero when the system boots and increases monotonically from there.* CLOCK_REALTIME which matches the current real-world time.The difference between the two clocks can be seen when the system time is adjusted, perhaps as a result of administrator action, tweaking by the network time protocol code, or suspending and resuming the system. In any of these situations,CLOCK_MONOTONIC will tick forward as if nothing had happened, while CLOCK_REALTIME may see discontinuous changes. Which clock should be used will depend mainly on whether the timer needs to be tied to time as the rest of the world sees it or not. The call to hrtimer_init() will tie an hrtimer to a specific clock, but that clock can be changed with:void hrtimer_rebase(struct hrtimer *timer, clockid_tnew_clock);hrtimer_start()-----------------------------Actually setting a timer is accomplished with:int hrtimer_start(struct hrtimer *timer,ktime_t time,enum hrtimer_mode mode);The mode parameter describes how the time parameter should be interpreted. A mode of HRTIMER_ABS indicates that time is an absolute value, while HRTIMER_REL indicates that time should be interpreted relative to the current time.。

相关主题