内核定时器

定时器的使用

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 1、综述 基于硬件工程师的提出的一个测试需求:每隔5秒钟拉高PA的使能脚,间隔5秒钟再拉低PA使能脚(这里的PA ...
    c枫_撸码的日子阅读 14,747评论 0 10
  • 主要内容:Linux-定时器 知识点: 1.Linux定时器基础知识 1.1 定时器的使用范围(延后执行某个操作,...
    嵌入式工作阅读 1,414评论 0 0
  • 内核定时器可用来在未来的某个时间点(基于时钟滴答)调度执行的某个函数。 当定时器运行时,调度定时器的进程可能正在休...
    rlkbk阅读 366评论 0 0
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,972评论 0 13
  • 今天老公去办了一件事,他自己成为了户主,真是值得开心的事情。 伴随着开学,今天又做了一件不应该的事,不该因为一点小...
    格格巫的小房子阅读 118评论 0 0