美文网首页
内核定时器

内核定时器

作者: 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等比较时间先 /后的宏完全可以放心地用于实际的代码中。

相关文章

  • 18-内存管理 01(定时器)

    一、定时器 NSProxy 二、GCD定时器 GCD定时器操作的系统内核,比NSTimer准时;当有scrollv...

  • 2018-07-26 定时器

    linux内核分析笔记----定时器和时间管理

  • SysTick定时器以及delay延迟函数(寄存器版)解析

    一、什么是SysTick 1、Systick定时器也叫滴答定时器或者系统定时器,是一个24位的内核级别的倒计数定时...

  • 时间、延迟与定时器

    1、内核计数器 时间戳计数器(TSC) 2、延迟 3、内核定时器 4、tasklet(小任务) 5、工作...

  • 读书笔记:LLD3(4)内核定时器

    内核定时器可用来在未来的某个时间点(基于时钟滴答)调度执行的某个函数。 当定时器运行时,调度定时器的进程可能正在休...

  • Linux 设备驱动之内核定时器 2020-02-20

    该内核定时器的实现是基于低精度定时器实现,高精度定时器的实现代码更为复杂,将在其他章节做相应介绍struct ti...

  • SysTick.

    SysTick: 系统定时器,24位,只能递减,存在于内核中,嵌套在NVIC中,所有的Cortex-M内核的单片机...

  • 9.20

    SysTick:系统定时器,24位,只能递减,存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具...

  • 芯灵思SinlinxA33开发板Linux内核定时器编程

    Linux 内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 ...

  • 内核定时器

    定时器的使用 1、定义定时器结构体timer_list。 2、设置超时时间,定义定时器处理函数和传参。 3、激活定...

网友评论

      本文标题:内核定时器

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