感谢支持
我们一直在努力

Linux hrtimer分析–未配置高精度模式

本文分析了Linux2.6.29中hrtimer的实现。


Linux2.6中实现了一种新的定时器hrtimer。与传统定时器使用时间轮算法不同,hrtimer使用了红黑树算法。hrtimer本身可以配置成高精度和普通精度两种,在单CPU系统和多CPU系统中的实现也有区别。这里先分析最简单的配置成普通精度、单CPU的情况。配置成高精度的情况见后续文章。


1.     时钟源的定义

为了实现hrtimer,Linux为系统中每一个CPU定义了一个hrtimer_cpu_base,这个结构体的定义如下: 


  1. struct hrtimer_cpu_base {  

  2.     raw_spinlock_t          lock;  

  3.     struct hrtimer_clock_base   clock_base[HRTIMER_MAX_CLOCK_BASES];  //时钟源 #define HRTIMER_MAX_CLOCK_BASES 2   

  4. #ifdef CONFIG_HIGH_RES_TIMERS   

  5.     ktime_t             expires_next;  

  6.     int             hres_active;  

  7.     int             hang_detected;  

  8.     unsigned long           nr_events;  

  9.     unsigned long           nr_retries;  

  10.     unsigned long           nr_hangs;  

  11.     ktime_t             max_hang_time;  

  12. #endif   

  13. };  

  14. <p> </p><pre class=“cpp” name=“code”>struct hrtimer_clock_base {  

  15.     struct hrtimer_cpu_base *cpu_base;  

  16.     clockid_t       index;  

  17.     struct rb_root      active;  

  18.     struct rb_node      *first;  

  19.     ktime_t         resolution;  

  20.     ktime_t         (*get_time)(void);  

  21.     ktime_t         softirq_time;  

  22. #ifdef CONFIG_HIGH_RES_TIMERS   

  23.     ktime_t         offset;  

  24. #endif   

  25. };  

在hrtimer.c中,有为每个CPU具体定义hrtimer_cpu_base的代码:


  1. DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =  

  2. {  

  3.   

  4.     .clock_base =  

  5.     {  

  6.         {  

  7.             .index = CLOCK_REALTIME,  

  8.             .get_time = &ktime_get_real,  

  9.             .resolution = KTIME_LOW_RES,  

  10.         },  

  11.         {  

  12.             .index = CLOCK_MONOTONIC,  

  13.             .get_time = &ktime_get,  

  14.             .resolution = KTIME_LOW_RES,  

  15.         },  

  16.     }  

  17. };  

1.1 时钟源类型


       可以看出,每个CPU都必须定义两个时钟源:REALTIME和MONOTONIC。REALTIME代表实时时钟,MONOTONIC代表单调递增时钟。两者的区别在于,当用户更改系统时间时,REALTIME时钟会收到影响,但MONOTONIC不受影响。这可以从它们两个的get_time函数指针看出来,REALTIME时钟指向的是ktime_get_real,MONOTONIC指向的是ktime_get。


     时钟源的结构体定义为struct hrtimer_clock_base,其中有两个域struct rb_node       *first和struct rb_root   active,这两个域维护了hrtimer的红黑树。也就是说,每一个hrtimer_clock_base都维护了自己的一个红黑树。


      hrtimer在初始化时,都需要加入到某一个时钟源的红黑树中,这个时钟源要么是REALTIME,要么是MONOTONIC,这个关联通过struct hrtimer的base域实现。


  1. struct hrtimer {  

  2.     struct rb_node          node;  

  3.     ktime_t             _expires;  

  4.     ktime_t             _softexpires;  

  5.     enum hrtimer_restart        (*function)(struct hrtimer *);  

  6.     struct hrtimer_clock_base   *base;  

  7.     unsigned long           state;  

  8. #ifdef CONFIG_TIMER_STATS   

  9.     int             start_pid;  

  10.     void                *start_site;  

  11.     char                start_comm[16];  

  12. #endif   

  13. };  

2. hrtimer的基本操作


      Linux的传统定时器通过时间轮算法实现(timer.c),但hrtimer通过红黑树算法实现。在struct hrtimer里面有一个node域,类型为struct rb_node,这个域代表了hrtimer在红黑树中的位置。


 2.1 hrtimer_start


hrtimer_start函数将一个hrtimer加入到一个按照到期时间排序的红黑树中,其主要流程为:


int hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode);


       // 根据time和mode参数的值计算hrtimer的超时时间,并设置到timer->expire域。


       // expire设置的是绝对时间,所以如果参数mode的值为HRTIMER_MODE_REL(即参数tim的值为相对时间),那么需要将tim的值修正为绝对时间:


       //     expire = tim + timer->base->get_time()。(注意本文只研究单CPU的情况)


       //调用enqueue_hrtimer将hrtimer加入到红黑树中。


 


2.2 hrtimer的到期


      hrtimer在hrtimer_run_queues函数中判断是否到期执行。hrtimer_run_queues <Called from hardirq context every jiffy>的调用链为:Linux的系统时钟函数->update_process_times->run_local_timers->hrtimer_run_queues。


void hrtimer_run_queues(void)


       // 判断是否是高精度模式,如果是高精度模式,立即返回。本文暂不考虑这种情况。


       // 对每一个时钟源(REALTIME和MONOTONIC)的红黑树,按到期先后顺序检查hrtimer,看它们是否到期(将定时器与时钟源的softirq_time比较)。如果到期,就把这个到期的定时器取出,然后按照定时器的具体模式执行相应的操作:


      //如果定时器模式为HRTIMER_CB_SOFTIRQ,那么将定时器搬到hrtimer_cpu_base的cb_pending队列


      //调用__run_hrtimer,在__run_hrtimer中执行定时器的回调函数。


 


在没有配置高精度模式时,cb_pending队列中的定时器会在TIMER_SOFTIRQ软中断中执行。调用链为


run_timer_softirq->
hrtimer_run_pending(Called from timer softirq every jiffy)->

hrtimer_switch_to_hres->
tick_init_highres->
tick_switch_to_oneshot(hrtimer_interrupt)
<把hrtimer_interrupt赋值给dev->event_handler,即dev->event_handler = handler;>


 


2.3 hrtimer_cancel


       hrtimer_cancel函数的作用是删除一个正在排队的定时器。这里分三种情况,一种是定时器已到期,并且设置了软中断模式;第二种是没有到期,还在红黑树中;第三种是定时器正在执行。


         第一种情况,定时器被挂在hrtimer_cpu_base的cb_pending队列中,所以需要把它从pending队列中移出。


         第二种情况,定时器还在红黑树中,那么把它从红黑树中移出。由于本文暂时只考虑高精度没有打开的情况,所以先不研究定时器正好排在红黑树第一个时的情况(即代码中调用hrtimer_force_reprogram函数的部分)。


         第三种情况删除失败,hrtimer_cancel函数会循环重试,等到定时器执行完的时候再删除。(这在多CPU系统中可能会发生)


3.  未使能高精度模式时与传统timer的区别

     1)传统timer使用时间轮算法,hrtimer使用红黑树算法


     2)传统timer在软中断中执行,hrtimer在硬中断中执行(update_process_times -> run_local_timers -> hrtimer_run_queues)。如果hrtimer设置了HRTIMER_CB_SOFTIRQ模式,那么timer会被移到pending队列,然后再由软中断执行。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux hrtimer分析–未配置高精度模式
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏