突兀地说高精度定时器,觉得摸不着脑子,起码初学者会沮丧,而且从字面上来理解,很简单,定时器嘛,精度低点,之后,就没有之后了。虽然差不多就是那么回事,只是上面涉及到了一些别的细节上的问题。
工欲善其事必先利其器,在开始讲之前,我们先利一下器:
2相关的用到的几个源代码文件以及其路径如下:
Hrtimers.txt(linux-3.2.12documentationtimers)
Hrtimer.c(linux-3.2.12kernel)
Hrtimer.h(linux-3.2.12includelinux)
2单纯的在高精度定时器模式下操作高精度定时器,整个操作框架如下:
初始化hrtimer_init,通过hetimer结构体设置相关数据,例如定时时长等->开启定时器hrtimer_start->运列宽精度定时器hrtimer_run_queues->触发中断,调用中断反弹函数,hrtimer_interrupt->移除高精度定时器remove_hrtimer.
读者现今脑袋里有一个框架,具体驱动细节下文将一一论述。
先概述一下,可能会有些拗口的东西在上面难理解,不要紧,旁边会有相关的代码和事例来解释。
?高精度定时器根据时间在一棵黑红树上排序。
?她们独立于周期时钟linux 应用定时器,采用脉宽时间戳而非jiffies的时间尺寸。
先把Linux代码上面的高精度定时器相关的文档掏出来,瞧瞧他的介绍,之后我再解释一下,文档路径:Hrtimers.txt(linux-3.2.12documentationtimers)
文档内容……说实话文档有点长,但是Linux的文档维护度不是很高,我从上面找了几句话翻译下来,之后解释一下:
?Thispatchintroducesanewsubsystemforhigh-resolutionkerneltimers.这句话里的patch这个词组有点意思,他是说高精度定时器作为一个补丁包被安装到系统里的,在2.6.16之前是没有这个概念的。
?第二点,英语太长就不贴了,是说为何要用高精度定时器,由于每位系统都存在定时器,其实精度不高,相比较而言就称之为低精度定时器。说白了就是须要高精度。
?第三点,高精度定时器还有个特征,就是他的框架在编译的时侯是在内核里,而且假如没有配置高精度定时器,这么高精度定时器是根据普通的定时器来运行。
?最后一点,高精度定时器是采用黑红树算法实现的,而普通的定时器是采用时间轮循算法实现的.
?另外,文档中还解释了好多诸如时钟源、数据结构、红黑树等等问题,这种问题在下边分开论述。
一、相关的数据结构
高帧率定时器所涉及的数据结构,我们从这几大方面考虑:
关于来源:就是说这个时钟是如何来的,在hrtimer.h中定义了一个结构体,代码如下:
structhrtimer_clock_base{
structhrtimer_cpu_base*cpu_base;
intindex;//用于区分时钟的属性(一共有两种,下边将会提到)
clockid_tclockid;//每位CPU所支持的时钟的ID
structtimerqueue_headactive;//正在启用的定时器的黑红树枝节点
ktime_tresolution;//时钟的帧率,毫秒
ktime_t(*get_time)(void);//拿来恢复当前时钟
ktime_tsoftirq_time;//在软中断中运列宽精度定时器队列的时间
ktime_toffset;//更改定时器时钟的额偏斜量
};
关于前面的几个元素,,有些东西解释一下。
高帧率定时器可以基于两种时钟(时钟基础,clockbase):一种是单调时钟(CLOCK_MONOTONIC),在系统启动时linux是什么系统,从0开始;另一种时钟(CLOCK_REALTIME)表示系统的实际时间。上文的结构体structhrtimer_clock_base中,index元素就是拿来分辨是CLOCK_MONOTONIC还是CLOCK_REALTIME时钟的。对于系统的每一个CPU都提供了一个包含这两种时钟基础的数据结构,每一个时侯总时钟基础都有一个黑红树,来排序所有待决的高精度定时器linux 应用定时器,而每位CPU都提供两个时钟基础(单调时钟和实际时间),所有定时器都按过期时间在黑红树上排序,假如定时器早已到期但其处理程序反弹函数仍未执行,则从黑红树迁移到一个数组中。在调整实时时钟的时侯,会导致储存在CLOCK_REALTIME时钟基础上的定时器的过期时间值与当前实际时间之间的误差。offset数组有助于修正这些情况,他表示定时器须要校准的偏斜量。因为这只是一种临时效应,极少发生。
在认识时钟源之前linux软件下载,我们或许还应当认识一个结构体structhrtimer,代码如下:
structhrtimer{
structtimerqueue_nodenode;//定时器队列节点,同时还管理node.expires,高精度定时器的绝对失效时间在其内部的算法这个时间和定时器基于的时钟有关(就是上文说的那两种时基).
ktime_t_softexpires;//绝对的最早的到期时间
enumhrtimer_restart(*)(structhrtimer*);//定时器到期反弹函数
structhrtimer_clock_base*base;//指向时基的表针(每位CPU,每位时钟)
unsignedlongstate;//状态信息,用来看位值
#ifdefCONFIG_TIMER_STATS
intstart_pid;//定时器统计区域储存的开始计时的任务的pid
void*start_site;//定时器储存当前定时的开始值
charstart_comm[16];//定时器统计区域名称开始计时的储存过程
#endif
};
以上这个结构体,对于用户来说,只需关心三点,第一是数组,这个是定时器失效后的反弹函数,第二是expires,这个表示到期时间,第三是最后一句话,高精度定时器结构体的使用必须经过hrtimer_init()函数的初始化,hrtimer_init()函数属于应用插口,所以放到前面说。这儿还有一个问题,也是高精度定时器的核心的问题,就是黑红树在高精度定时器的应用问题,其实现今讲这个有点早,而且先让读者心中有那么个底,Linux的传统定时器通过时间轮算法实现(timer.c),但hrtimer通过黑红树算法实现。在structhrtimer上面有一个node域,类型为structtimerqueue_node,这个域代表了hrtimer在黑红树中的位置,注意一下,我参考的源代码是3.2.12版本,在2.6.X版本中这个域的格式为structrb_node。这儿先跟读者打声招呼,有那么回事,等在将具体用法时,我们在谈谈是怎样实现的。
两个重要的结构体说完了,因为要兼容多核处理器,因而会涉及到每位CPU的时基,结构体structhrtimer_cpu_base就是拿来定义每位CPU的时钟的,目前每位CPU只对应于单调时钟和实时时钟,结构体如下:
structhrtimer_cpu_base{//单个CPU时基结构体
raw_spinlock_tlock;//锁定相关的时基和定时器,载流子锁
unsignedlongactive_bases;//用活动的定时器标记基础的位数组
#ifdefCONFIG_HIGH_RES_TIMERS
ktime_texpires_next;//即将到期的下一个时间的绝对时间
inthres_active;//高帧率模式的状态,布尔变量
inthang_detected;//最新的被发觉的挂起的高精度定时器中断
unsignedlongnr_events;//高精度定时器中断总量
unsignedlongnr_retries;//高精度定时器中断重试总量
unsignedlongnr_hangs;//高精度定时器中断挂起总量
ktime_tmax_hang_time;//在高精度定时器中断触发最长时间
#endif
structhrtimer_clock_baseclock_base[HRTIMER_MAX_CLOCK_BASES];//此CPU时基表针
};
里面的三个结构体应当是最基础的,定义了高精度定时器相关的功能和元素,而且每一个CPU都全套一个定义的结构体,之后初始化hrtimers。
基本的结构体讲完了,下边开始讲API插口。
首先是配置并初始化hrtimers的API。在开始的时侯我们讲structhrtimer的时侯,提及要使用structhrtimer要先初始化,函数申明代码如下:
voidhrtimer_init(structhrtimer*timer,clockid_tclock_id,
enumhrtimer_modemode)//给定时钟初始化定时器
debug_init(timer,clock_id,mode);
__hrtimer_init(timer,clock_id,mode);
以上函数实现了一个高精度定时器的初始化,下边是相关元素的解释:
/**
*hrtimer_init–给定时钟初始化定时器
*@timer:即将被初始化的定时器
*@clock_id:即将被用到的时钟
*@mode:定时器模式abs/rel
*/
mode可以使用五个常数,如下:
enumhrtimer_mode{
HRTIMER_MODE_ABS=0x0,/*时间是绝对的*/
HRTIMER_MODE_REL=0x1,/*时间是相对的*/
HRTIMER_MODE_PINNED=0x02,/*定时器被绑定到CPU*/
HRTIMER_MODE_ABS_PINNED=0x02,
HRTIMER_MODE_REL_PINNED=0x03,
};
hrtimer_init()函数上面调用了__hrtimer_init()函数,下边是该函数的原型:
staticvoid__hrtimer_init(structhrtimer*timer,clockid_tclock_id,enumhrtimer_modemode)
structhrtimer_cpu_base*cpu_base;
intbase;
memsettimer,0,sizeof(structhrtimer));
cpu_base=&__raw_get_cpu_var(hrtimer_bases);
if(clock_id==CLOCK_REALTIME&&mode!=HRTIMER_MODE_ABS)
clock_id=CLOCK_MONOTONIC;
base=hrtimer_clockid_to_base(clock_id);
timer->base=&cpu_base->clock_base[base];
timerqueue_init(&timer->node);
#ifdefCONFIG_TIMER_STATS
timer->start_site=NULL;
timer->start_pid=-1;
memset(timer->start_comm,0,TASK_COMM_LEN);
#endif
__hrtimer_init()函数调用了structhrtimer_cpu_base结构体对CPU进行相关的初始化,并使用memset()函数,原型如下:
void*memset(void*s,intc,size_tn)
inti;
char*ss=s;
for(i=0;i ss[i]=c; returns; 这个函数清空了memory上面的东西,完成了初始化,memset(timer,0,sizeof(structhrtimer))。 在这儿注意一下,还是上面说到的一个问题,我用的源代码是3.2.12的,而2.6.X的源代码里所提供的,虽然只有两个常数
以上是高精度定时器的相关介绍及操作框架解析的详细内容。更多信息请关注PHP中文网其他相关文章!