linux内核定时器
来源:互联网 发布:为什么淘宝评论删不掉 编辑:程序博客网 时间:2024/05/10 23:28
Linux内核定时器,一个硬件逻辑单元,系统的定时器硬件,定时一一定周期频率输出方波,给CPU产生定时器中断。以某种频率自行触发时钟中断,其频率可通过变成预定。
在内核中,有一个对应的时钟中断的处理程序,这个中断处理程序一般会做下面的事情:
1. 更新系统的运行时间(jiffies);
2. 更新实际时间(年月日时分秒);
3. 检查进程的时间片,如果时间片用尽了,将重新调度;
4. 检查有没有超时的定时器,更新资源消耗和CPU时间的统计值。
我们系统的时间没有百分之百精准的,因为我们的进程是放在一个链表里面等待执行的,因此系统要遍历链表,而这点时间就产生了误差,我们只能尽量减少这种误差。还记得我们的开发板的时钟每次掉电后时间就不对了,这是因为掉电以后有一个纽扣电池给RTC(硬件)供电,但是我们的纽扣电池显然已经没电了。。。RTC是用来维护时间的硬件
下面是几个重要的概念:
1. 节拍率HZ,内核的一个常数,系统的定时器频率,与体系结构相关,系统启动根据HZ设置定时器硬件,描述一秒钟硬件定时器发生了多少次中断,这个值最终会写入硬件定时器中,HZ=100,表示1s(ARM平台)产生100次时钟中断,在32位系统中为1000次。
2. 节拍tick:HZ的倒数,每发生一次硬件定时器中断的时间间隔,1tick = 1/HZ = 10ms(HZ=100)。
3. jiffies:内核的全局变量(32位,unsigned long),被用来记录开机以来,发生了多少次时钟中断,每发生一次时钟中断(tick数),jiffies加1,一般使用它来描述当前时间。如:unsigned long timeout = jiffies + 3 * HZ,timeout表示3s以后的时间。jiffies的定义为:extern unsigned long volatile jiffies; 其中volatile关键字是为了防止系统对此数据进行优化,即每次访问jiffies都从内存去访问,而不是访问寄存器。顺便提一下,访问的速度由高到底为:寄存器 > cache > 内存 > 外存
jiffies回绕问题
我们知道,jiffies是内核定义的全局变量,它的值会一直自增,它在内核中定义为无符号长整型,因此在32位系统中最大值为32^2-1,在64位系统中为64^2-1,所以,当jiffies增加到最大值时,又会回到0,这时候和系统运行的时间当然就不对了,这就是回绕问题,
这里有个例子说明一下:
unsigned long timeout = jiffies + HZ / 2;/*do something*/..................../*check timeout*/if(timeout > jiffies){ /*not timeout*/}else{ /*it is timeout!*/}
这个程序用timeout来检测程序的执行是否超时,timeout设为jiffies加上0.5秒,如果jiffies回环了,那么程序就会出错。
因此,linux定义了下面四个比较函数
#define time_after(jiffies, timeout) ((long)(known) - (long)(unknown) < 0)#define time_before(jiffies, timeout) ((long)(known) - (long)(unknown) < 0)#define time_after_eq(jiffies, timeout) ((long)(known) - (long)(unknown) >= 0)#define time_before_eq(jiffies, timeout) ((long)(known) - (long)(unknown) >= 0)
可以看出这里是将无符号的类型换成了有符号的类型,因此这样就可以降到负数,那么负数从补码变为原码,这时再比较原码就能得出正确结果,下面举两个例子:
无符号数据jiffies为250,补码为11111010,timeout为252,补码为11111100,过一会儿jiffies变为1,即00000001,这时候明显两次的jiffies数据都比timeout小,结果与实际不符合
有符号数据jiffies为250,补码转为原码(符号位不变,其余取反,最后加1,要进位):10000110,十进制为-6;有符号数据timeout为252,补码转为原码:10000100,十进制为-4,过一会儿jiffies变为1,补码转为原码:00000001,即为1,此时timeout的值刚好夹在中间,那么就巧妙地解决了回绕问题。
内核定时器能指定某个函数(定时器函数)在特定的未来某个时刻执行,且内核定时器注册的处理函数只执行一次,不是循环的。
内核定时器定义:
<linux/timer.h>struct timer_list{ struct list_head entry; //链表头,内核维护 unsigned long expires; //超时时候jiffies的值,jiffies + 5*HZ void(*function)(unsigned long); //超时处理函数 unsigned long data; //内核调用超时处理函数时传递给他的参数,一般为指针 struct tvec_base *base; //内核维护}
分配一个定时器:
struct timer_list timer;
初始化定时器:
init_timer(&timer); //内核只初始化自己关心的字段time.expires = jiffies + 5 * HZ; //设置超时时间为5s以后timer.function = mytimer_function; //设置超时处理函数timer.data = (unsigned long)&mydata; //给函数传递的参数
启动定时器:
add_time(&timer); //一旦定时器到期,内核会自动删除定时器,处理函数只能被执行一次
删除定时器:
del_timer(&timer);
修改定时器:
mod_timer(&timer, 新的超时时候的jiffies值);mod_timer(&timer, jiffies + 10 * HZ);mod_timer = del_timer + expires = 新值 + add_timer;
如果想重复循环执行定时器的处理函数,只需在超时处理函数中重新启动定时器即可。
下面是一些关于定时器的代码例程:
定时器控制led闪烁间隔:
/********************************************************************************* * Copyright: (C) 2017 tangyanjun<519656780@qq.com> * All rights reserved. * * Filename: timer.c * Description: This file * * Version: 1.0.0(08/21/2017) * Author: tangyanjun <519656780@qq.com> * ChangeLog: 1, Release initial version on "08/21/2017 08:38:55 PM" * ********************************************************************************/#include <linux/init.h>#include <linux/module.h>#include <linux/timer.h>#include <asm/gpio.h>#include <plat/gpio-cfg.h>static struct timer_list mytimer;static int mydata = 0x5555;static void mytimer_function(unsigned long data){ static int i = 0; if(i == 100) { i = 0; } else if((i % 2) == 0) { gpio_direction_output(S3C2410_GPB(5), 1); } else { gpio_direction_output(S3C2410_GPB(5), 0); } i++; //重复循环执行超时处理函数 mod_timer(&mytimer, jiffies + 2 * HZ); //此时定时器的超时时间为0 //add_timer(&mytimer); //mytimer.expires = jiffies + 2 * HZ; //add_timer(&mytimer); //因为此执行有可能被别的任务所打断,比如中断,如果在中断处理函数中对mytimer进行操作,最后引起出错,这个执行路劲不是原子的}static int mytimer_init(void){ //初始化定时器 init_timer(&mytimer); //指定定时器的超时时间 mytimer.expires = jiffies + 2 * HZ; //指定定时器的超时处理函数 mytimer.function = mytimer_function; //给超时处理函数传递的参数 mytimer.data = (unsigned long)&mydata; gpio_request(S3C2410_GPB(5), "LED1"); gpio_direction_output(S3C2410_GPB(5), 0); // gpio_set_value(S3C2410_GPB(5), 0); //启动定时器 add_timer(&mytimer); printk("Start Timer!\n"); printk("Led on\n"); return 0;}static void mytimer_exit(void){ gpio_set_value(S3C2410_GPB(5), 1); gpio_free(S3C2410_GPB(5)); //删除定时器 del_timer(&mytimer);}module_init(mytimer_init);module_exit(mytimer_exit);MODULE_LICENSE("GPL");
- linux-内核-内核定时器
- Linux嵌入式 -- 内核 - 内核定时器
- Linux 内核开发 - 内核定时器
- Linux内核定时器
- linux 内核定时器
- linux内核定时器分析
- Linux内核定时器
- Linux内核定时器
- Linux 2.6 内核定时器
- linux内核定时器
- Linux内核定时器
- linux内核定时器编程
- linux内核定时器
- Linux内核定时器
- linux内核定时器
- linux 内核定时器编程
- linux 内核定时器
- linux内核定时器
- IDEA sliksvn提交代码提示no changes detected
- Python爬取OJ提交过的代码
- LeetCode(279)Perfect Squares
- maven配置
- mysql中CONCAT_WS的用法
- linux内核定时器
- GJJ来签到
- UVa 636
- Leetcode-Intersection of Two Linked Lists-Python
- XCode开发学习
- 判断字符串中是否含有关键词(关键词会有多个,英文逗号隔开)
- libevent札记
- 阿里前CEO卫哲:我特别反对无人便利店 凤凰新闻 08-24 10:55 原标题:阿里前CEO卫哲:我特别反对无人便利店 卫哲,32岁就成长为史上最年轻的世界500强中国区总裁,36岁成为阿里巴巴CE
- 手势控制音量、亮度