Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1833560
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-02-15 15:27:53

定时器
 
谨以此文纪念过往的岁月。
 
一.前言
在linux中,软件定时器最终会依赖硬件定时器来实现。内核在时钟中断发生后检测各定时器是否到期,到期的定时器处理函数会作为软中断在底部执行。
二.定时器使用
2.1定义定时器
struct timer_list xx_timer;
struct timer_list {
 struct list_head entry;   --定时器链表
 unsigned long expires;    --定时到期时间
 void (*function)(unsigned long); --定时器到期后处理函数
 unsigned long data;  --作为传入处理函数的参数
 struct tvec_base *base;
};
2.2初始化定时器
init_timer      函数初始化定时器
TIMER_INITIALIZER() 宏定义初始化定时器
DEFINE_TIMER 定义和初始化定时器成员
#define TIMER_INITIALIZER(_function, _expires, _data) {  \
  .entry = { .prev = TIMER_ENTRY_STATIC }, \  
  .function = (_function),   \
  .expires = (_expires),    \
  .data = (_data),    \
  .base = &boot_tvec_bases,   \   --对于这个应该很好理解了。
 }
#define DEFINE_TIMER(_name, _function, _expires, _data)  \
 struct timer_list _name =    \
  TIMER_INITIALIZER(_function, _expires, _data)
  
struct tvec_base {    --这个是用于smp,多CPU的情况。
 spinlock_t lock;   --同步锁
 struct timer_list *running_timer;  --当前运行的定时器
 unsigned long timer_jiffies;  
 struct tvec_root tv1;   --下面均为timer容器,针对于不同到期时间,将不同的定时器分别存到不同的容器中。其实是为了加速CPU查询快到期的定时器。
 struct tvec tv2;
 struct tvec tv3;
 struct tvec tv4;
 struct tvec tv5;
} ____cacheline_aligned;
struct tvec_base boot_tvec_bases;
2.3 增加定时器
add_timer -> _mod_timer
int __mod_timer(struct timer_list *timer, unsigned long expires)
{
 struct tvec_base *base, *new_base;
 unsigned long flags;
 int ret = 0;
 timer_stats_timer_set_start_info(timer);  --退化为空
 BUG_ON(!timer->function);
 base = lock_timer_base(timer, &flags);  --锁定timer->base->lock
 if (timer_pending(timer)) {   --如果timer处在 pending 状态(在 base 中,准备执行),则卸载timer
  detach_timer(timer, 0);
  ret = 1;
 }
 debug_timer_activate(timer);
 new_base = __get_cpu_var(tvec_bases);   --获取CPU base
 if (base != new_base) {   --如果该时钟被切换到其他CPU
  if (likely(base->running_timer != timer)) {  --判断timer是否运行
   timer_set_base(timer, NULL);   --重新设置timer->base
   spin_unlock(&base->lock);
   base = new_base;
   spin_lock(&base->lock);
   timer_set_base(timer, base);
  }
 }
 timer->expires = expires;    --设置timer到期时间
 internal_add_timer(base, timer); --将timer添加到base中,即是将timer根据距离到期的ticks来分配不同的容器。
 spin_unlock_irqrestore(&base->lock, flags);  --解锁
 return ret;
}
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
 unsigned long expires = timer->expires;
 unsigned long idx = expires - base->timer_jiffies;  --判断距离到期的ticks
 struct list_head *vec;
 if (idx < TVR_SIZE) {
  int i = expires & TVR_MASK;
  vec = base->tv1.vec + i;
 } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
  int i = (expires >> TVR_BITS) & TVN_MASK;
  vec = base->tv2.vec + i;
 } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
  int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
  vec = base->tv3.vec + i;
 } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
  int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
  vec = base->tv4.vec + i;
 } else if ((signed long) idx < 0) {
  vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
 } else {
  int i;
  if (idx > 0xffffffffUL) {
   idx = 0xffffffffUL;
   expires = idx + base->timer_jiffies;
  }
  i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
  vec = base->tv5.vec + i;
 }
 list_add_tail(&timer->entry, vec);   --添加到链表尾,FIFO
}
2.4删除定时器
del_timer
该函数与上述函数配对,删除定时器仅仅将定时器从对应的base链表中删除。
int del_timer(struct timer_list *timer)
{
 struct tvec_base *base;
 unsigned long flags;
 int ret = 0;
 timer_stats_timer_clear_start_info(timer);
 if (timer_pending(timer)) {
  base = lock_timer_base(timer, &flags);
  if (timer_pending(timer)) {
   detach_timer(timer, 1);
   ret = 1;
  }
  spin_unlock_irqrestore(&base->lock, flags);
 }
 return ret;
}
2.5修改定时器的expire
mod_timer -> _mod_timer
定时器的使用就是如上。
三.定时器原理详解
linux中软定时器是采用软中断实现的。在硬件定时器中断中,会采用唤醒软中断,然后在中断的底半部来实现定时器的处理。
在系统初始化时,会调用init_timers,在这个函数中我们只关注open_softirq
void __init init_timers(void)
{
 int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,(void *)(long)smp_processor_id());
 init_timer_stats();
 BUG_ON(err == NOTIFY_BAD);
 register_cpu_notifier(&timers_nb);
 open_softirq(TIMER_SOFTIRQ, run_timer_softirq);  --打开一个软中断,run_timer_softirq为中断底半部处理函数。
}
在linux的硬件定时器中,中断处理函数为timer_tick。我们主要是关注软timer实现,对于硬件timer,暂时不管。
void timer_tick(void)
{
 profile_tick(CPU_PROFILING);
 do_leds();   --如果设置了定时器led
 do_set_rtc();  --更新rtc
 write_seqlock(&xtime_lock);
 do_timer(1);
 write_sequnlock(&xtime_lock);
 update_process_times(user_mode(get_irq_regs()));
}
void update_process_times(int user_tick)
{
 struct task_struct *p = current;
 int cpu = smp_processor_id();
 /* Note: this timer irq context must be accounted for as well. */
 account_process_tick(p, user_tick);
 run_local_timers();
 if (rcu_pending(cpu))
  rcu_check_callbacks(cpu, user_tick);
 printk_tick();
 scheduler_tick();
 run_posix_cpu_timers(p);
}
void run_local_timers(void)
{
 hrtimer_run_queues();
 raise_softirq(TIMER_SOFTIRQ);  --触发软中断
 softlockup_tick();
}
在每一次的硬件中断中,都会触发一次软中断。那看一下软中断的函数处理的底半部。
static void run_timer_softirq(struct softirq_action *h)
{
 struct tvec_base *base = __get_cpu_var(tvec_bases);  --获取CPU base
 hrtimer_run_pending();
 if (time_after_eq(jiffies, base->timer_jiffies)) --判断jiffies是否大于base的jiffies
  __run_timers(base);
}
static inline void __run_timers(struct tvec_base *base)
{
 struct timer_list *timer;
 spin_lock_irq(&base->lock);
 while (time_after_eq(jiffies, base->timer_jiffies)) {
  struct list_head work_list;
  struct list_head *head = &work_list;
  int index = base->timer_jiffies & TVR_MASK;
  if (!index &&(!cascade(base, &base->tv2, INDEX(0))) &&(!cascade(base, &base->tv3, INDEX(1))) &&!cascade(base, &base->tv4, INDEX(2)))cascade(base, &base->tv5, INDEX(3));
  ++base->timer_jiffies;
  list_replace_init(base->tv1.vec + index, &work_list);  --替换list
  while (!list_empty(head)) {                           
   void (*fn)(unsigned long);
   unsigned long data;
   timer = list_first_entry(head, struct timer_list,entry);
   fn = timer->function;
   data = timer->data;
   timer_stats_account_timer(timer);
   set_running_timer(base, timer);  --设置当前base running_timer 为timer
   detach_timer(timer, 1);          --删除timer
   spin_unlock_irq(&base->lock);
   {
    int preempt_count = preempt_count(); 
    fn(data);                               --调用定时器处理函数
    if (preempt_count != preempt_count()) { --在定时器函数运行时不能发生阻塞和抢占。否则触发异常
     BUG();
    }
   }
   spin_lock_irq(&base->lock);
  }
 }
 set_running_timer(base, NULL);
 spin_unlock_irq(&base->lock);
}
定时器就OK了。
阅读(2344) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~