感谢支持
我们一直在努力

Linux内核分析之软定时器笔记

定时器是一种软件功能,即允许在将来的某个时刻,函数在给定的时间间隔用完时被调用。超时表示与定时器相关的时间间隔已经用完的那个时刻。


linux上考虑两种类型的定时器,即动态定时和间隔定时器。第一种类型由内核使用,而间隔定时器由进程在用户态创建。


动态定时器


动态定时的主要数据结构是一个叫做tvec_bases的per cpu变量,他包含NR_CPUS个元素,系统中每个CPU都有一个。每个元素是一个tvec_base_t类型的数据结构,他包含相应CPU中处理动态定时器需要的所有数据。


  1. struct tvec_base {  

  2.     spinlock_t lock;  

  3.     struct timer_list *running_timer;  

  4.     unsigned long timer_jiffies;  

  5.     unsigned long next_timer;  

  6.     struct tvec_root tv1;  

  7.     struct tvec tv2;  

  8.     struct tvec tv3;  

  9.     struct tvec tv4;  

  10.     struct tvec tv5;  

  11. } ____cacheline_aligned;  


字段tv1的数据解雇为tvec_root_t类型,包含一个vec数组,这个数组由256个list_head元素组成(即256个动态定时器链表组成)。这个结构包含了在紧接着到来的255个节拍内将要到期的所有动态定时器。


字段tv2,tv3和tv4的数据结构都是tvec_t类型,该类型有一个数组vec。这些链表包含在紧接着到来的2^14-1/2^20-1以及2^26-1个节拍内将要到期的所有动态定时器。


字段tv5与前面的字段几乎相同,但唯一区别就是vec数组的最后一项是一个大expires字段值得动态定时器链表。tv5从不需要从其他的数组补充。



动态定时器编程


1,申请timer_list结构并对其初始化,其中必须初始化的有expires,function


  1. struct timer_list {  

  2.     struct list_head entry;  

  3.     unsigned long expires;  

  4.   

  5.     void (*function)(unsigned long);  

  6.     unsigned long data;  

  7.   

  8.     struct tvec_base *base;  

  9. #ifdef CONFIG_TIMER_STATS   

  10.     void *start_site;  

  11.     char start_comm[16];  

  12.     int start_pid;  

  13. #endif   

  14. #ifdef CONFIG_LOCKDEP   

  15.     struct lockdep_map lockdep_map;  

  16. #endif   

  17. };  

2,调用init_timer函数初始化


该函数最终调用下面函数


  1. static void __init_timer(struct timer_list *timer,  

  2.              const char *name,  

  3.              struct lock_class_key *key)  

  4. {  

  5.     timer->entry.next = NULL;  

  6.     timer->base = __raw_get_cpu_var(tvec_bases);  

  7. #ifdef CONFIG_TIMER_STATS   

  8.     timer->start_site = NULL;  

  9.     timer->start_pid = -1;  

  10.     memset(timer->start_comm, 0, TASK_COMM_LEN);  

  11. #endif   

  12.     lockdep_init_map(&timer->lockdep_map, name, key, 0);  

  13. }  

可看到初始化的几个相关变量。

3,调用add_timer函数将申请的timer_list结构添加到合适的链表


该函数最终调用如下函数添加到对应的链表


  1. static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)  

  2. {  

  3.     unsigned long expires = timer->expires;  

  4.     unsigned long idx = expires – base->timer_jiffies;  

  5.     struct list_head *vec;  

  6.   

  7.     if (idx < TVR_SIZE) {  

  8.         int i = expires & TVR_MASK;  

  9.         vec = base->tv1.vec + i;  

  10.     } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {  

  11.         int i = (expires >> TVR_BITS) & TVN_MASK;  

  12.         vec = base->tv2.vec + i;  

  13.     } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {  

  14.         int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;  

  15.         vec = base->tv3.vec + i;  

  16.     } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {  

  17.         int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;  

  18.         vec = base->tv4.vec + i;  

  19.     } else if ((signed long) idx < 0) {  

  20.         /* 

  21.          * Can happen if you add a timer with expires == jiffies, 

  22.          * or you set a timer to go off in the past 

  23.          */  

  24.         vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);  

  25.     } else {  

  26.         int i;  

  27.         /* If the timeout is larger than 0xffffffff on 64-bit 

  28.          * architectures then we use the maximum timeout: 

  29.          */  

  30.         if (idx > 0xffffffffUL) {  

  31.             idx = 0xffffffffUL;  

  32.             expires = idx + base->timer_jiffies;  

  33.         }  

  34.         i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;  

  35.         vec = base->tv5.vec + i;  

  36.     }  

  37.     /* 

  38.      * Timers are FIFO: 

  39.      */  

  40.     list_add_tail(&timer->entry, vec);  

  41. }  

其中对于定时器的修改、删除等操作,内核提供了相应的函数。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux内核分析之软定时器笔记
分享到: 更多 (0)

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

支付宝扫一扫打赏

微信扫一扫打赏