zoukankan      html  css  js  c++  java
  • linux 定时器原理

    内核定时器:
        unsigned long timeout = jiffies + (x * HZ);
        while(1) {
            // Check the condition.
            // Take a schedule.
            if (time_after(jiffies, timeout)) {
                printk("Timeout ");
                break;
            }
        }
    转换到秒:    
    s = (jiffies - last_jiffies)/HZ;
    jiffies(约50天溢出)为jiffies_64的后32位,因此直接读取jiffies_64不具备原子性,使用get_jiffies_64,
    函数原理:[平台为32位则需要保护读取,否则直接读取]
    顺序锁:        读读/读写并发,写写互斥
    读写自旋锁:    读读并发,读写/写写互斥
    自旋锁:        不允许任何操作并发
    u64 get_jiffies_64(void)
    {
        unsigned long seq;
        u64 ret;
        do {
            seq = read_seqbegin(&xtime_lock);
            ret = jiffies_64;
        } while (read_seqretry(&xtime_lock, seq)); // 若读的过程中发生写,则重读
        return ret;
    }
    这里涉及一个顺序锁的读写规则:
    读不会更改lock的seq,写会++,这里就会发现到值被写覆盖,于是重新读。

    write_seqlock(&lock);
    ...... //写操作代码
    write_sequnlock(&lock);
    顺序锁的使用场景是必须默认保持写互斥后,才能使用顺序锁.


    睡眠延时:
    这种睡眠再调度的精度要低于jiffies的精度:
    schedule_timeout(xx);
    函数原理:
        expire = timeout + jiffies;            // 超时截至时间
        setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
        __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
        schedule();
        del_singleshot_timer_sync(&timer);
        destroy_timer_on_stack(&timer);
        timeout = expire - jiffies;
     out:
        return timeout < 0 ? 0 : timeout;
        
    从这里看出,这个函数实际上是基于timer来实现的

    标准的延时调度接口 timer_list:[如果需要循环调度,则在timer_func中递归init/add]
    struct timer_list my_timer;
    init_timer(&my_timer);
    my_timer.expire = jiffies + n*HZ;
    my_timer.function = timer_func;
    my_timer.data = func_parameter;
    add_timer(&my_timer);


    短延时(基于指令级忙等,不基于jiffy机制的方法):
    mdelay/udelay/ndelay
    基于一个全局的变量:loops_per_jiffy,变量初始化位于:
    calibrate_delay()
    基本原理是,先从4096 以*2的倍数找到第一个范围 4096*x < t < 4096*2x
    然后逐渐开始细化,从4096*x 开始,逐渐递增4096*x>>2(而不是减半),直到到达对应的精度要求
    10000000
    11000000
    10100000
    ....

    static inline void __udelay(unsigned long usecs)
    {
        unsigned long loops_per_usec;
        loops_per_usec = (loops_per_jiffy * HZ) / 1000000;        //一秒中能够执行的指令数目/1000000(ms)
        __delay(usecs * loops_per_usec);                        //Delay 几毫秒的可以执行的指令
    }


    注:rdtsc这个指令是得到CPU自启动以后的运行周期,不适合超线程和多核CPU

    墙上时钟:RTC
    static struct timeval curr_time;
    do_gettimeofday(&curr_time);


    timer_list原理:
    初始化timer时,首先取一个cpu变量【timer在哪个cpu注册,就在哪个cpu触发】
    作为本cpu上所有timer的控制结构,根据超时程度将timers进行分级管理,其中base->timer_jiffies为最短的那个计时器的时间:
    0 - 1<<8       tv1  index = expires
      - 1<<(8+6)   tv2    index = expires >> 8()
      - 1<<(8+2*6) tv3  index = expires >> 8+6()
      - 1<<(8+3*6) tv4  index = expires >> 8+2*6()
      ...          tv5  [不是直接根据索引来决定在数组的地方,因为数组的地方是有限的]
     

    在tv2中,需要把 2^8 - 2^14之间的timers均匀放到一个2^6的数组中,只能2^8对齐,每个数组链中最多放置2^8个timers.
    往后类推..


    tv1
    1  2  3  4  5  6 ....    vec
    a1 b1 c1 d1 e1 f1
    a2 b2 c2
    a3    c3
    a4
    最后一步,添加timer到目标队列的尾部.

    timer_list执行调度:
    初始化:init_timers_cpu,主要是分配cpu变量,(除了启动cpu0是固定的静态空间),后再初始化tv1-tv5所有的timer header.
    调用:这里从时钟irq中开始执行,增加jiffies.
    run_timer_softirq(timer.c)
       ->  __run_timers
       
    在函数update_process_times调用run_local_timers后触发软中断:
    raise_softirq(TIMER_SOFTIRQ);  

    遍历过程:
    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);        //清空这个列表,并处理
            while (!list_empty(head)) {                                    //遍历这个列表下的所有timer
                void (*fn)(unsigned long);
                unsigned long data;
                bool irqsafe;
                timer = list_first_entry(head, struct timer_list,entry);//取出timer
                fn = timer->function;
                data = timer->data;
                irqsafe = tbase_get_irqsafe(timer->base);

                timer_stats_account_timer(timer);

                base->running_timer = timer;
                detach_expired_timer(timer, base);                        //timer脱链

                if (irqsafe) {
                    spin_unlock(&base->lock);
                    call_timer_fn(timer, fn, data);                        //调用实际的函数
                    spin_lock(&base->lock);
                } else {
                    spin_unlock_irq(&base->lock);
                    call_timer_fn(timer, fn, data);
                    spin_lock_irq(&base->lock);
                }
            }
        }
        base->running_timer = NULL;
        spin_unlock_irq(&base->lock);
    }

    核心的降级处理函数:
    #define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

    static int cascade(struct tvec_base *base, struct tvec *tv, int index)
    {
        struct timer_list *timer, *tmp;
        struct list_head tv_list;
        list_replace_init(tv->vec + index, &tv_list);            //获取
        list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
            __internal_add_timer(base, timer);
        }
        return index;
    }

    index=0 说明当前的tv1已经为空,这个时候base->timer_jiffies应该已经 >256, INDEX(N)的作用就是减去基数获取实际所在的
    链表位置,在tv2中timer_jiffies逐渐增加,每次取tv2的一个数组链表然后释放到tv1中(256),逐渐释放,当tv2结束时,同理从tv3
    释放到tv2.

  • 相关阅读:
    Java学习开篇
    《我的姐姐》
    世上本无事,庸人自扰之
    这48小时
    补觉
    淡定
    es java api 设置index mapping 报错 mapping source must be pairs of fieldnames and properties definition.
    java mongodb groupby分组查询
    linux 常用命令
    mongodb too many users are authenticated
  • 原文地址:https://www.cnblogs.com/wenhuisun/p/3158625.html
Copyright © 2011-2022 走看看