zoukankan      html  css  js  c++  java
  • linux内核中的定时器代码简介

        struct timer_list :软件时钟,记录了软件时钟的到期时间以及到期后要执行的操作。具体的成员以及含义见表3-1。
        struct tvec_base :用于组织、管理软件时钟的结构。在 SMP 系统中,每个 CPU 有一个。具体的成员以及含义参见表3-2。

        表3-1 struct timer_list 主要成员
    域名     类型     描述
    entry     struct list_head     所在的链表
    expires     unsigned long     到期时间,以 tick 为单位
    function     void (*)(unsigned long)     回调函数,到期后执行的操作
    data     unsigned long     回调函数的参数
    base     struct tvec_base *     记录该软件时钟所在的 struct tvec_base 变量

        表3-2 struct tvec_base 类型的成员
    域名     类型     描述
    lock     spinlock_t     用于同步操作
    running_timer     struct timer_list *     正在处理的软件时钟
    timer_jiffies     unsigned long     当前正在处理的软件时钟到期时间
    tv1     struct tvec_root     保存了到期时间从 timer_jiffies 到 timer_jiffies + 浅析Linux 时钟处理机制(图二)之间(包括边缘值)的所有软件时钟
    tv2     struct tvec     保存了到期时间从 timer_jiffies + 浅析Linux 时钟处理机制(图三)到 timer_jiffies +浅析Linux 时钟处理机制(图四)之间(包括边缘值)的 所有软件时钟
    tv3     struct tvec     保存了到期时间从 timer_jiffies +浅析Linux 时钟处理机制(图五)到 timer_jiffies +浅析Linux 时钟处理机制(图六)之间(包括边缘值)的所有软件时钟
    tv4     struct tvec     保存了到期时间从 timer_jiffies +浅析Linux 时钟处理机制(图七)到 timer_jiffies +浅析Linux 时钟处理机制(图八)之间(包括边缘值)的所有软件时钟
    tv5     struct tvec     保存了到期时间从 timer_jiffies +浅析Linux 时钟处理机制(图九)到 timer_jiffies +浅析Linux 时钟处理机制(图十)之间(包括边缘值)的所有软件时钟

        其中 tv1 的类型为 struct tvec_root ,tv 2~ tv 5的类型为 struct tvec ,清单3-1显示它们的定义


        清单3-1 struct tvec_root 和 struct tvec 的定义

    struct tvec {
    struct list_head vec[TVN_SIZE];
    };

    struct tvec_root {
    struct list_head vec[TVR_SIZE];
    };

    可见它们实际上就是类型为 struct list_head 的数组,其中 TVN_SIZE 和 TVR_SIZE 在系统没有配置宏 CONFIG_BASE_SMALL 时分别被定义为64和256。

    /*
     * 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();

        //首先在软中断处理中判断jiffies如果大于最近的定时器的到期时间base->timer_jiffies,就表示有定时器到期了
        //进入__run_timers(base)进行处理
        if (time_after_eq(jiffies, base->timer_jiffies))
            __run_timers(base);
    }

    /**
     * __run_timers - run all expired timers (if any) on this CPU.
     * @base: the timer vector to be processed.
     *
     * This function cascades all vectors and executes all expired timer
     * vectors.
     */
    static inline void __run_timers(tvec_base_t *base)
    {
        struct timer_list *timer;

        spin_lock_irq(&base->lock);
       
        //判断jiffies是否大于当前定时器到期时间base->timer_jiffies,
        //base->timer_jiffies也就是到期时间最短的那个
        while (time_after_eq(jiffies, base->timer_jiffies)) {
            struct list_head work_list;
            struct list_head *head = &work_list;
           
            //最近定时器在tv1中的index,也就是jiffies的低6位
            int index = base->timer_jiffies & TVR_MASK;   

            /*
             * Cascade timers:
             */
            //如果tv1中index==0,表示tv1中定时器全部过时了,需要从tv2~tv5中取出256个jiffies的定时器填充tv1
            //也就是说每过256个tick,就会cascade一次,下面再介绍cascade
            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));
               
            //为什么加1?
            ++base->timer_jiffies;
            //tv1在index位置的定时器队列,用work_list指向它,并把tv1中index的队列头设为空
            list_replace_init(base->tv1.vec + index, &work_list);
            //如果取出的定时器队列不为空,需要处理定时器
            while (!list_empty(head)) {
                void (*fn)(unsigned long);
                unsigned long data;
               
                //找第一个timer
                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);                //设为正在运行的timer
                detach_timer(timer, 1);                        //删除这个timer
                spin_unlock_irq(&base->lock);                //在执行timer回调函数时,先释放lock
                {
                    int preempt_count = preempt_count();
                    fn(data);                                //执行timer的函数
                    if (preempt_count != preempt_count()) {
                        printk(KERN_WARNING "huh, entered %p "
                               "with preempt_count %08x, exited"
                               " with %08x?\n",
                               fn, preempt_count,
                               preempt_count());
                        BUG();
                    }
                }
                spin_lock_irq(&base->lock);
            }
        }
        set_running_timer(base, NULL);                        //把正在运行的timer设为空
        spin_unlock_irq(&base->lock);
    }

    //取出tv2~tv5的定时器,放到tv1中
    static int cascade(tvec_base_t *base, tvec_t *tv, int index)
    {
        /* cascade all the timers from tv up one level */
        struct timer_list *timer, *tmp;
        struct list_head tv_list;

        list_replace_init(tv->vec + index, &tv_list);

        /*
         * We are removing _all_ timers from the list, so we
         * don't have to detach them individually.
         */
        list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
            BUG_ON(tbase_get_base(timer->base) != base);
            internal_add_timer(base, timer);
        }

        return index;
    }

    //tv2对应INDEX(0),即(jiffies>>8)&0x3f
    //tv3对应INDEX(1),即(jiffies>>14)&0x3f
    //tv4对应INDEX(2),即(jiffies>>20)&0x3f
    //tv5对应INDEX(3),即(jiffies>>26)&0x3f
    #define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

  • 相关阅读:
    VTK 9.0.1 vtkContextDevice2D 问题
    VTK 中文
    VTK 剪切
    VTK Color Map
    VTK Camera
    VTK Light
    VTK Read Source Object
    VTK Procedural Source Object
    Qt 布局开发问题记录
    Grafana 系列 (7):圖表是否可以数据追踪 (drill down)?(转)
  • 原文地址:https://www.cnblogs.com/GoodGoodWorkDayDayUp/p/1866110.html
Copyright © 2011-2022 走看看