内核定时器 struct timer_list

定时器

定时器,有时也称为动态定时器或内核定时器,是管理内核时间的基础。内核经常要推后执行某些代码,比如下半部机制就是为了将工作推后执行。我们需要一种工具,使工作能够在指定时间点上执行,正好在希望的时间点上,内核定时器正是这样一种工具。

定时器使用简单,只须执行一些初始化工作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器就可以了。

注意,定时器并不周期运行,它在超时后就自行销毁,这就是这种定时器被称为动态定时器的原因。动态定时器不断地创建和销毁,而且它的运行次数也不受限制。

  • 使用定时器

定时器由结构timer_list表示:

  1. struct timer_list {
        struct list_head entry; //定时器链表的入口
         unsigned long expires; //定时器超时时的节拍数
        void (*function)(unsigned long); //定时器处理函数
         unsigned long data; //传给定时器处理函数的长整型参数
        struct tvec_t_base_s *base; //定时器内部值,用户不要使用
    #ifdef CONFIG_TIMER_STATS
        void *start_site;
        char start_comm[16];
        int start_pid;
    #endif
    };


  2. struct tvec_t_base_s {
         spinlock_t lock;
        struct timer_list *running_timer;
         unsigned long timer_jiffies;
         tvec_root_t tv1;
         tvec_t tv2;
         tvec_t tv3;
         tvec_t tv4;
         tvec_t tv5;
    } ____cacheline_aligned_in_smp;

    内核提供了一组与定时器相关的接口用来简化管理定时器的操作。所有这些接口都声明在中,大多数接口在中的到实现。

创建定时器首先要先定义它,然后通过一个辅助函数初始化定时器数据结构的内部值,初始化必须在使用其他定时器管理函数之前完成

  1. struct timer_list my_timer;
    init_timer(&my_timer);// 初始化定时器
    my_timer.expires = jiffies + delay;//
    my_timer.data = 0;//
    my_timer.function = my_function;//
    add_timer(&my_timer);//最后,激活定时器

    【补充】

    也可以这样调用:

    1、setup_timer(struct timer_list, function,data); //初始化timer并赋值function和data

    2、mod_timer();// 修改并启动之。另外,定时值可以这样设定:msecs_to_jiffies(50);

    调用方式:

    mod_timer(&mytimer, jiffies + msecs_to_jiffies(50) );


    处理函数的原型:

void my_timer_function(unsigned long data);

data参数可以使你利用同一个处理函数注册多个定时器,只须通过该参数就能区别对待他们。如果你不需要这个参数,可以简单传递0或任何其他值给处理函数。

  1. /**
    * init_timer - initialize a timer.
    * @timer: the timer to be initialized
    *
    * init_timer() must be done to a timer prior calling *any* of the
    * other timer functions.
    */
    void fastcall init_timer(struct timer_list *timer)
    {
         timer->entry.next = NULL;
         timer->base = __raw_get_cpu_var(tvec_bases);
    #ifdef CONFIG_TIMER_STATS
         timer->start_site = NULL;
         timer->start_pid = -1;
         memset(timer->start_comm, 0, TASK_COMM_LEN);
    #endif
    }
    /**
    * add_timer - start a timer
    * @timer: the timer to be added
    *
    * The kernel will do a ->function(->data) callback from the
    * timer interrupt at the ->expires point in the future. The
    * current time is 'jiffies'.
    *
    * The timer's ->expires, ->function (and if the handler uses it, ->data)
    * fields must be set prior calling this function.
    *
    * Timers with an ->expires field in the past will be executed in the next
    * timer tick.
    */
    static inline void add_timer(struct timer_list *timer)
    {
         BUG_ON(timer_pending(timer));
         __mod_timer(timer, timer->expires);
    }
    int __mod_timer(struct timer_list *timer, unsigned long expires)
    {
         tvec_base_t *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);
        if (timer_pending(timer)) {
             detach_timer(timer, 0);
             ret = 1;
         }
         new_base = __get_cpu_var(tvec_bases);
        if (base != new_base) {
            /*
              * We are trying to schedule the timer on the local CPU.
              * However we can't change timer's base while it is running,
              * otherwise del_timer_sync() can't detect that the timer's
              * handler yet has not finished. This also guarantees that
              * the timer is serialized wrt itself.
              */
            if (likely(base->running_timer != timer)) {
                /* See the comment in lock_timer_base() */
                 timer->base = NULL;
                 spin_unlock(&base->lock);
                 base = new_base;
                 spin_lock(&base->lock);
                 timer->base = base;
             }
         }
         timer->expires = expires;
         internal_add_timer(base, timer);
         spin_unlock_irqrestore(&base->lock, flags);
        return ret;
    }

    当前节拍计数等于或大于指定的超时时,内核就开始执行定时器处理函数。因为内核有可能延误定时器的执行,所以不能用定时器来实现任何硬实时任务。

有时可能需要更改已经激活的定时器超时时间,内核通过mod_timer()来实现该功能。当定时器已经初始化,但未激活,mod_timer()就激活它。一旦从该函数返回,定时器都被激活而且设置了新的定时值。

  1. /**
    * mod_timer - modify a timer's timeout
    * @timer: the timer to be modified
    * @expires: new timeout in jiffies
    *
    * mod_timer() is a more efficient way to update the expire field of an
    * active timer (if the timer is inactive it will be activated)
    *
    * mod_timer(timer, expires) is equivalent to:
    *
    *      del_timer(timer); timer->expires = expires; add_timer(timer);
    *
    * Note that if there are multiple unserialized concurrent users of the
    * same timer, then mod_timer() is the only safe way to modify the timeout,
    * since add_timer() cannot modify an already running timer.
    *
    * The function returns whether it has modified a pending timer or not.
    * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
    * active timer returns 1.)
    */
    int mod_timer(struct timer_list *timer, unsigned long expires)
    {
         BUG_ON(!timer->function);
         timer_stats_timer_set_start_info(timer);
        /*
          * This is a common optimization triggered by the
          * networking code - if the timer is re-modified
          * to be the same thing then just return:
          */
        if (timer->expires == expires && timer_pending(timer))
            return 1;
        return __mod_timer(timer, expires);
    }

    如果要在定时器超时之前停止定时器,使用del_timer()函数:
  1. /**
    * del_timer - deactive a timer.
    * @timer: the timer to be deactivated
    *
    * del_timer() deactivates a timer - this works on both active and inactive
    * timers.
    *
    * The function returns whether it has deactivated a pending timer or not.
    * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
    * active timer returns 1.)
    */
    int del_timer(struct timer_list *timer)
    {
         tvec_base_t *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;
    }

    被激活或未被激活的定时器都可以使用该函数。注意不要为已经超时的定时器调用该函数,因为它们会自动被删除

删除定时器时要消息,可能存在潜在的竞争条件。多处理器机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等待可能在其他处理器上运行的定时器处理程序都退出,这是要使用del_timer_sync()函数执行删除工作:

  1. /**
    * del_timer_sync - deactivate a timer and wait for the handler to finish.
    * @timer: the timer to be deactivated
    *
    * This function only differs from del_timer() on SMP: besides deactivating
    * the timer it also makes sure the handler has finished executing on other
    * CPUs.
    *
    * Synchronization rules: Callers must prevent restarting of the timer,
    * otherwise this function is meaningless. It must not be called from
    * interrupt contexts. The caller must not hold locks which would prevent
    * completion of the timer's handler. The timer's handler must not call
    * add_timer_on(). Upon exit the timer is not queued and the handler is
    * not running on any CPU.
    *
    * The function returns whether it has deactivated a pending timer or not.
    */
    int del_timer_sync(struct timer_list *timer)
    {
        for (;;) {
            int ret = try_to_del_timer_sync(timer);
            if (ret >= 0)
                return ret;
             cpu_relax();
         }
    }

    和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用。

  • 定时器竞争条件

定时器与当前执行代码是异步的,因此有可能存在潜在的竞争条件。

首先,绝不能用下列代码来代替mod_timer()函数:

  1. del_timer(my_timer);
  2. my_timer->expires = jiffies + new_delay;
  3. add_timer(my_timer);

其次,使用del_timer_sync()函数取代del_timer()函数。

最后,因为内核异步执行中断处理程序,所以应该重点保护定时器中断处理程序中的共享数据。

  • 实现定时器

内核在时钟中断发生后执行定时器,定时器作为软中断在下半部上下文中执行。

具体来说,时钟中断处理程序会执行update_process_timers()函数,该函数随即调用run_local_timers()函数:

  1. /*
    * Called from the timer interrupt handler to charge one tick to the current
    * process.   user_tick is 1 if the tick is user time, 0 for system.
    */
    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. */
        if (user_tick)
             account_user_time(p, jiffies_to_cputime(1));
        else
             account_system_time(p, HARDIRQ_OFFSET, jiffies_to_cputime(1));
         run_local_timers();
        if (rcu_pending(cpu))
             rcu_check_callbacks(cpu, user_tick);
         scheduler_tick();
         run_posix_cpu_timers(p);
    }
    /*
    * Called by the local, per-CPU timer interrupt on SMP.
    */
    void run_local_timers(void)
    {
         raise_softirq(TIMER_SOFTIRQ);
         softlockup_tick();
    }
    run_timer_softirq()函数处理软中断TIMER_SOFTIRQ,从而在当前处理器上运行所有的超时定时器:
    /*
    * This function runs timers and the timer-tq in bottom half context.
    */
    static void run_timer_softirq(struct softirq_action *h)
    {
         tvec_base_t *base = __get_cpu_var(tvec_bases);
         hrtimer_run_queues();
        if (time_after_eq(jiffies, base->timer_jiffies))
             __run_timers(base);
    }

    所有定时器都以链表形式存放在一起,为了提高搜索效率,内核将定时器按它们的超时时间划分为五组。当定时器超时时间接近时,定时器将随组一起下移。采用分组定时器的方法可以在执行软中断的多数情况下,确保内核尽可能减少搜索超时定时器所带来的负担。


原文地址:http://hi.baidu.com/leicejbgnndjrze/item/69dbcb8207a134ddd1f8cde7


你可能感兴趣的:(嵌入式linux)