美文网首页
内核定时器

内核定时器

作者: gbmaotai | 来源:发表于2018-09-03 10:16 被阅读0次

    定时器的使用

    1、定义定时器结构体timer_list。

    2、设置超时时间,定义定时器处理函数和传参。

    3、激活定时器。

    1、定义并初始化定时器结构体timer_list。

    /*include/linux/timer.h*/
    struct timer_list {
    struct list_head entry;
    unsigned long expires; //设置在执行定时器处理函数的时间
    void (*function)(unsigned long); //定时器处理函数
    unsigned long data; //处理函数的传参
    ...
    
    静态定义并初始化:

    初始化结构体的同时给指定测成员赋值
    动态初始化

    struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
    
    动态初始化:
    /*定义一个名为my_timer的timer_list数据结构*/
    struct timer_list my_timer;
    /*初始化my_timer的部分内部成员*/
    init_timer(&my_timer);
    

    2、设置超时时间,定义定时器处理函数和传参。

    这一步骤是要填充timer_list的三个成员:expires、function和data。

    void timer_func(unsigned long data) //2.定义定时器处理函数
    {
    printk("time out![%d] [%s]\n", (int)data, current->comm);
    }
    然后给timer_list的三个成员赋值:
    
    my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5秒
    my_timer.function = timer_func; //2.给结构体指定定时器处理函数
    my_timer.data = (unsigned long)99; //2.设定定时器处理函数的传参
    
    当前时间(jiffies)的后5秒(5*HZ)
    

    3、激活定时器。

    add_timer(&my_timer); //3.激活定时器
    
    int mod_timer(struct timer_list *timer, unsigned long expires)
    //这是改变定时器超时时间的函数,如果在指定的定时器(timer)没超时前调用,超时时间会更新为新的新的超时时间(expires)。如果在定时器超时后调用,那就相当于重新指定超时时间并再次激活定时器。
    
    irqreturn_t irq_handler(int irqno, void *dev_id)
    15 {
    16 printk("irq\n");
    17 /*0.5秒触发一次定时器处理函数,则只有最后一次抖动的中断会触发timer_func*/
    18 mod_timer(&my_timer, jiffies + 100); //0.5*200 = 100
    19 return IRQ_HANDLED; //表示中断在处理,IRQ_NONE表示中断没处理
    20 }
    

    每次进入中断,都会刷新定时器的值。当按下按键时,由于抖动的关系,会出现多次的中断,但只有最后的一次中断才会触发一次定时器处理函数。

    jiffies 回绕

    全局变量#### jiffies 用来记录自启动以来产生的节拍的总数。系统启动时会将该变量初始化为0,此后,每当时钟中断产生时就会增加该变量的值。 jiffies和另外一个变量息息相关:HZ。HZ是每秒系统产生的时钟中断次数,所以jiffies每秒增加的值也就是HZ.
    在2.6内核中被定义为1000
    extern unsigned long volatile jiffies;
    如果HZ为1000,那么回绕时间将只有50天左 右。如果发生了回绕现象,对内核中直接利用jiffies来比较时间的代码将产生很不利的影响,

    内核也专门针对这种情况提供了四个宏来帮助比较jiffies计数:

      #define time_after(unknown,known) ((long)(known) - (long)(unknown)<0)
      #define time_before(unkonwn,known) ((long)(unknown) - (long)(known)<0)
      #define time_after_eq(unknown,known) ((long)(unknown) - (long)(known)>=0)
      #define time_before_eq(unknown,known) ((long)(known) -(long)(unknown)>=0)
    
    原码

    原码采 用一个最高位作为符号位,其余位是数据大小的二进制表示。
    正数的补码即为原码,负数的补码为原码除符号位外其他各位取反再加1

    unsigned long timeout = jiffies + HZ/2; /* timeout in 0.5s */  
      
    /* do some work ... */  
    do_somework();  
      
    /* then see whether we took too long */  
    if (time_before(jiffies, timeout)) {  
    /* we did not time out, call no_timeout_handler() ... */  
    no_timeout_handler();  
      
    } else {  
    /* we timed out, call timeout_handler() ... */  
    timeout_handler();  
    }  
    
    time_after等比较时间先后的宏背后的原理

    上述time_after等比较时间先/后的宏为什么能够解决jiffies溢出造成的错误情况呢?
    我们仍然以8位无符号整型(unsigned char)为例来加以说明。仿照上面的time_after宏,我们可以给出简化的8位无符号整型对应的after宏:

    define uc_after(a, b) ((char)(b) - (char)(a) < 0)

    设a和b的数据类型为unsigned char,b为临近8位无符号整型最大值附近的一个固定值254,下面给出随着a(设其初始值为254)变化而得到的计算值:

    a b (char)(b) - (char)(a)
    254 254 0
    255 - 1
    0 - 2
    1 - 3
    ...
    124 -126
    125 -127
    126 -128
    127 127
    128 126
    ...
    252 2
    253 1

    从上面的计算可以看出,设定b不变,随着a(设其初始值为254)不断增长1,a的取值变化为:
    254, 255,一次产生溢出
    0, 1, ..., 124, 125, 126, 127, 126, ..., 253, 254, 255, (二次产生溢出)
    0, 1, ...
    ...

    而(char)(b) - (char)(a)的变化为:
    0, -1,
    -2, -3, ..., -126, -127, -128, 127, 126, ..., 1, 0, -1,
    -2, -3, ...
    ...

    从上面的详细过程可以看出,当a取值为254,255, 接着在(一次产生溢出)之后变为0,然后增长到127之前,uc_after(a,b)的结果都显示a是在b之后,这也与我们的预期相符。但在a取值为 127之后,uc_after(a,b)的结果却显示a是在b之前。

    从上面的运算过程可以得出以下结论:
    使用uc_after(a,b)宏来计算两个8位无符号整型a和b之间的大小(或先/后,before/after),那么a和b的取值应当满足以下限定条件:

    两个值之间相差从逻辑值来讲应小于有符号整型的最大值。

    比如: 对于8位无符号整型,两个值之间相差从逻辑值来讲应小于128。

    对于32位无符号整型,两个值之间相差从逻辑值来讲应小于2147483647。

    对于HZ=100,那么两个时间值之间相差不应当超过2147483647/100秒 = 0.69年 = 248.5天。对于HZ=60,那么两个时间值之间相差不应当超过2147483647/60秒 = 1.135年。

    在实际代码应用中,需要比较先/后的两个时间值之间一般都相差很小,范围大致在1秒~1天左右,所以以上time_after等比较时间先 /后的宏完全可以放心地用于实际的代码中。

    相关文章

      网友评论

          本文标题:内核定时器

          本文链接:https://www.haomeiwen.com/subject/wohbwftx.html