内核26X中的时钟与定时器.docx
《内核26X中的时钟与定时器.docx》由会员分享,可在线阅读,更多相关《内核26X中的时钟与定时器.docx(5页珍藏版)》请在冰点文库上搜索。
![内核26X中的时钟与定时器.docx](https://file1.bingdoc.com/fileroot1/2023-5/7/2e7d9ba2-1fa2-4099-8b59-f505eb9b9293/2e7d9ba2-1fa2-4099-8b59-f505eb9b92931.gif)
内核26X中的时钟与定时器
内核2.6.X中的时钟和定时器
2007年07月11日星期三15:
47
时钟和定时器对Linux内核来说十分重要。
首先内核要管理系统的运行时间(uptime)和当前墙上时间(walltime),即当前实际时间。
其次,内核中大量的活动由时间驱动(timedriven)。
其中一些活动是周期性的,比如调度调度器(scheduler)中的运行队列(runqueue)或者刷新屏幕这样的活动,它们以固有的频率定时发生;同时,内核要非周期性地调度某些函数在未来某个时间发生,比如推迟执行的磁盘I/O操作等。
实时时钟
---------------------------------------------------------
内核必须借助硬件来实现时间管理。
实时时钟(realtimeclock)是用来持久存放系统时间的设备,它和CMOS集成在一起,并通过主板电池供电,所以即便在关闭计算机系统之后,实时时钟仍然能继续工作。
系统启动时,内核读取实时时钟,将所读的时间存放在变量xtime中作为墙上时间(walltime),xtime保存着从1970年1月1日0:
00到当前时刻所经历的秒数。
虽然在Intelx86机器上,内核会周期性地将当前时间存回实时时钟中,但应该明确,实时时钟的主要作用就是在启动时初始化墙上时间xtime。
系统定时器和动态定时器
---------------------------------------------------------
周期性发生的事件都是由系统定时器(systemtimer)驱动。
在X86体系结构上,系统定时器通常是一种可编程硬件芯片(如8254CMOS芯片),又称可编程间隔定时器(PIT,ProgrammableIntervalTimer),其产生的中断就是时钟中断(timerinterrupt)。
时钟中断对应的处理程序负责更新系统时间和执行周期性运行的任务。
系统定时器的频率称为节拍率(tickrate),在内核中表示为HZ。
以X86为例,在2.4之前的内核中其大小为100;从内核2.6开始,HZ=1000,也就是说每秒时钟中断发生1000次。
这一变化使得系统定时器的精度(resolution)由10ms提高到1ms,这大大提高了系统对于时间驱动事件调度的精确性。
过于频繁的时钟中断不可避免地增加了系统开销(overhead),但是总的来说,在现在计算机系统上,HZ=1000不会导致难以接受的系统开销。
和系统定时器相对的是动态定时器(dynamictimer),它是调度事件(执行调度程序)在未来某个时刻发生的时机。
内核可以动态地创建或销毁动态定时器。
系统定时器及其中断处理程序是内核管理机制的中枢,下面是一些利用系统定时器周期执行的工作(中断处理程序所做的工作):
(1)更新系统运行时间(uptime)
(2)更新当前墙上时间(walltime)
(3)在对称多处理器系统(SMP)上,均衡调度各处理器上的运行队列
(4)检查当前进程是否用完了时间片(timeslice),如果用尽,则进行重新调度
(5)运行超时的动态定时器
(6)更新资源耗尽和处理器时间的统计值
内核动态定时器依赖于系统时钟中断,因为只有在系统时钟中断发生后内核才会去检查当前是否有超时的动态定时器。
X86体系结构中时钟资源还包括CPU本地APIC(localAdvancedProgrammableInterruptController)中的定时器和时间戳计时器TSC(TimeStampCounter)。
高精度定时器将使用CPU本地APIC作为高精度定时中断源。
高精度定时器的设计和实现
---------------------------------------------------------
X86体系结构中,内核2.6.X的HZ=1000,即系统时钟中断执行粒度为1ms,这意味着系统中周期事情最快为1ms执行一次,而不可能有更高的精度。
动态定时器随时都可能超时,但由于只有在系统时钟中断到来时内核才会检查执行超时的动态定时器,所以动态定时器的平均误差大约为半个系统时钟周期(即0.5ms).
对于实时要求较高的电信使用来说,普通Linux在实时性方面和电信平台的要求之间还存在一定的差距。
CGL为了增强Linux的软实时能力,在以下方面对内核进行了改进:
提供高精度的动态定时器;提供可抢占式内核(preemptionkernel)等。
下面主要介绍高精度实时器的设计思想及实现,该实现遵循PISIX1003.1b中时钟和定时器相关API标准,方便使用程序开发人员的使用。
高精度定时器的基本设计思想为:
用(jiffies+sub_jiffie)表示动态定时器的超时时间,PIT仍然按频率HZ=1000产生系统时钟中断。
如果在一个时钟中断tick和下一个时钟中断(tick+1)之间,即[jiffies,jiffies+1)之间,有高精度动态定时器等待处理(超时时间表示为(jiffies+sub_jiffie),sub_jiffie<1),那么用最近的动态定时器超时值sub_jiffie对硬件定时器(PIT或localAPIC)进行设定,使其在时刻(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的大小。
Thehigh-resolutiontimerAPI
---------------------------------------------------------
LastSeptember,thispagefeaturedanarticleonthektimerspatchbyThomasGleixner.Thenewtimerabstractionwasdesignedtoenabletheprovisionofhigh-resolutiontimersinthekernelandtoaddresssomeoftheinefficienciesencounteredwhenthecurrenttimercodeisusedinthismode.Sincethen,therehasbeenalargeamountofdiscussion,andthecodehasseensignificantwork.Theendproductofthatwork,nowcalled"hrtimers,"wasmergedforthe2.6.16release.
Atitscore,thehrtimermechanismremainsthesame.Ratherthanusingthe"timerwheel"datastructure,hrtimersliveonatime-sortedlinkedlist,withthenexttimertoexpirebeingattheheadofthelist.Aseparatered/blacktreeisalsousedtoenabletheinsertionandremovaloftimereventswithoutscanningthroughthelist.Butwhilethecoreremainsthesame,justabouteverythingelsehaschanged,atleastsuperficially.
structktime_t
-----------------------------
Thereisanewtype,ktime_t,whichisusedtostoreatimevalueinnanoseconds.Thistype,foundin,ismeanttobeusedasanopaquestructure.And,interestingly,itsdefinitionchangesdependingontheunderlyingarchitecture.On64-bitsystems,aktime_tisreallyjusta64-bitintegervalueinnanoseconds.On32-bitmachines,however,itisatwo-fieldstructure:
one32-bitvalueholdsthenumberofseconds,andtheotherholdsnanoseconds.Theorderofthetwofieldsdependsonwhetherthehostarchitectureisbig-endianornot;theyarealwaysarrangedsothatthetwovaluescan,whenneeded,betreatedasasingle,64-bitvalue.Doingthingsthiswaycomplicatestheheaderfiles,butitprovidesforefficienttimevaluemanipulationonallarchitectures.
struct--ktime_t
typedefunion{
On64-bitsystems
|----------------------|
| s64tv64; |
|----------------------|
On32-bitmachines
|----------------------|
| struct{ |
| s32sec,nsec;|
| s32nsec,sec;|
| }tv; |
|----------------------|
}ktime_t;
初始化ktime_t
-----------------------------
Awholesetoffunctionsandmacroshasbeenprovidedforworkingwithktime_tvalues,startingwiththetraditionaltwowaystodeclareandinitializethem(ktime_tvalues):
(1)Initializetozero
DEFINE_KTIME(name);
(2)ktime_tkt;
kt=ktime_set(longsecs,longnanosecs);
初始化高精度时钟hrtimer
-----------------------------
Theinterfaceforhrtimerscanbefoundin.Atimerisrepresentedbystructhrtimer,whichmustbeinitializedwith:
voidhrtimer_init(structhrtimer*timer,clockid_twhich_clock);
Systemclocks
-----------------------------
Everyhrtimerisboundtoaspecificclock.Thesystemcurrentlysupportstwoclocks,being:
*CLOCK_MONOTONIC:
aclockwhichisguaranteedalwaystomoveforwardintime,butwhichdoesnotreflect"wallclocktime"inanyspecificway.Inthecurrentimplementation,CLOCK_MONOTONICresemblesthejiffiestickcountinthatitstartsatzerowhenthesystembootsandincreasesmonotonicallyfromthere.
*CLOCK_REALTIMEwhichmatchesthecurrentreal-worldtime.
Thedifferencebetweenthetwoclockscanbeseenwhenthesystemtimeisadjusted,perhapsasaresultofadministratoraction,tweakingbythenetworktimeprotocolcode,orsuspendingandresumingthesystem.Inanyofthesesituations,CLOCK_MONOTONICwilltickforwardasifnothinghadhappened,whileCLOCK_REALTIMEmayseediscontinuouschanges.Whichclockshouldbeusedwilldependmainlyonwhetherthetimerneedstobetiedtotimeastherestoftheworldseesitornot.Thecalltohrtimer_init()willtieanhrtimertoaspecificclock,butthatclockcanbechangedwith:
voidhrtimer_rebase(structhrtimer*timer,clockid_tnew_clock);
hrtimer_start()
-----------------------------
Actuallysettingatimerisaccomplishedwith:
inthrtimer_start(structhrtimer *timer,
ktime_t time,
enumhrtimer_modemode);
Themodeparameterdescribeshowthetimeparametershouldbeinterpreted.AmodeofHRTIMER_ABSindicatesthattimeisanabsolutevalue,whileHRTIMER_RELindicatesthattimeshouldbeinterpretedrelativetothecurrenttime.