【Linux开发】linux设备驱动归纳总结(七):2.内核定时器
linux设备驱动归纳总结(七):2.内核定时器
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
这节将介绍内核定时器的使用。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、定时器
之前说过两类跟时间相关的内核结构。
1、延时:通过忙等待或者睡眠机制实现延时。
2、tasklet和工作队列,通过某种机制使工作推后执行,但不知道执行的具体时间。
接下来要介绍的定时器,能够使工作在指定的时间点上执行,而且不需要使用忙等待这类的延时方法。通过定义一个定时器,告之内核在哪个时间需要执行什么函数就可以了,等时间一到,内核会就执行指定的函数。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、使用定时器
定时器的使用很简单,只需要三部:
1、定义定时器结构体timer_list。
2、设置超时时间,定义定时器处理函数和传参。
3、激活定时器。
接下来一步步来说。
、定义并初始化定时器结构体timer_list。
/*include/linux/timer.h*/
11 struct timer_list {
12 struct list_head entry;
13 unsigned long
expires; //设置在执行定时器处理函数的时间
14
15 void (*function)(unsigned
long); //定时器处理函数
16 unsigned long
data; //处理函数的传参
17
18 struct tvec_base *base;
19 #ifdef CONFIG_TIMER_STATS
20 void *start_site;
21 char start_comm[16];
22 int start_pid;
23 #endif
24 };
红色部分是待会我们要自己赋值的,其他内核帮忙搞定。
这个也是有静态和动态区分,步骤如下:
静态定义并初始化:
struct
timer_list TIMER_INITIALIZER(_function, _expires, _data);
初始化结构体的同时给指定测成员赋值
动态初始化:
/*定义一个名为my_timer的timer_list数据结构*/
struct timer_list my_timer;
/*初始化my_timer的部分内部成员*/
init_timer(&my_timer);
、设置超时时间,定义定时器处理函数和传参。
这一步骤是要填充timer_list的三个成员:expires、function和data。
三定时器处理函数的要求定义一个定时器处理函数:
/*7th_time_2/1st/test.c*/
9 void timer_func(unsigned long data) //2.定义定时器处理函数
10 {
11 printk("time out![%d] [%s]\n", (int)data, current->comm);
12 }
然后给timer_list的三个成员赋值:
/*7th_time_2/1st/test.c*/
18 my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5秒
19 my_timer.function = timer_func; //2.给结构体指定定时器处理函数
20 my_timer.data = (unsigned long)99; //2.设定定时器处理函数的传参
这里要注意一下,expires是指定定时器处理函数在什么时候触发,我这里定义在当前时间(jiffies)的后5秒(5*HZ)。
3、激活定时器。
其实就一个函数,一旦调用,内核就会知道,在当前时候的5秒后,执行相应的处理函数。
/*7th_time_2/1st/test.c*/
22 add_timer(&my_timer); //3.激活定时器
这里要注意,定时器激活后,它只会在指定时间执行一次处理函数,执行后会将定时器在内核中移除。
这样就大功告成了,给个完整代码:
/*7th_time_2/1st/test.c*/
1 #include
2 #include
3
4 #include
5 #include
6
7 struct timer_list
my_timer; //1.定义定时器结构体timer_list
8
9 void timer_func(unsigned
long data) //2.定义定时器处理函数
10 {
11 printk("time out![%d] [%s]\n", (int)data, current->comm);
//打印当前进程
12 }
13
14 static int __init test_init(void) //模块初始化函数
15 {
16 init_timer(&my_timer);
//1.初始化timer_list结构
17
18 my_timer.expires
= jiffies + 5*HZ; //2.秒
19 my_timer.function
= timer_func; //2.给结构体指定定时器处理函数
20 my_timer.data
= (unsigned long)99; //2.设定定时器处理函数的传参
21
22 add_timer(&my_timer); //3.激活定时器
23 printk("hello timer,current->comm[%s]\n", current->comm);
24 return 0;
25 }
26
27 static void __exit test_exit(void) //模块卸载函数
28 {
29 printk("good bye timer\n");
30 }
在看看效果:
[root: 1st]# insmod test.ko
hello timer,current->comm[insmod]
[root: 1st]# time out![99] [swapper] //五秒后打印
[root: 1st]# rmmod test
good bye timer
这里要注意的是,当执行处理函数是,当前进程是swapper,而不是加载模块时的insmod,也就说明,当调用add_timer后,就会将定时器交由内核来管理,当时间一到,内核调用进程swapper来执行处理函数。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、定时器的删除和修改
上面说了,激活定时器后只能执行一遍,如果要实现隔指定时间又重复执行,那就要修改一下代码。
在定时器处理函数中加上两条代码:
/*7th_time_2/2nd/test.c*/
14 my_timer.expires = jiffies + 2*HZ; //重新设定时间,在两秒后再执行
15 add_timer(&my_timer); //再次激活定时器
这样的话,每个2秒就会再次执行定时器处理函数。
这两条代码也相当与一下的函数:
/*7th_time_2/2nd/test.c*/
17 mod_timer(&my_timer, jiffies + 2*HZ);
/*kernel/timer.c*/
689 int mod_timer(struct timer_list *timer, unsigned long expires)
这是改变定时器超时时间的函数,如果在指定的定时器(timer)没超时前调用,超时时间会更新为新的新的超时时间(expires)。如果在定时器超时后调用,那就相当于重新指定超时时间并再次激活定时器。
如果想在定时器没有超时前取消定时器,可以调用以下函数:
/*kernel/timer.c*/
718 int del_timer(struct timer_list *timer)
该函数用来删除还没超时的定时器。
不贴个代码:
/*7th_time_2/2nd/test.c*/
7 struct timer_list my_timer; //1.定义定时器结构体timer_list
8
9 void timer_func(unsigned long data) //2.定义定时器处理函数
10 {
11 printk("time out![%d] [%s]\n", (int)data, current->comm);
12
13 #if 0
14 my_timer.expires
= jiffies + 2*HZ;
15 add_timer(&my_timer);
16 #endif
17 mod_timer(&my_timer,
jiffies + 2*HZ); //mod_timer相当于14.15行两步
18 }
19
20 static int __init test_init(void) //模块初始化函数
21 {
22 init_timer(&my_timer); //1.初始化timer_list结构
23
24 my_timer.expires = jiffies + 5*HZ; //2.设定定时器处理函数触发时间为5秒
25 my_timer.function = timer_func; //2.给结构体指定定时器处理函数
26 my_timer.data = (unsigned long)99; //2.设定定时器处理函
数的传参
27
28 add_timer(&my_timer); //3.激活定时器
29 printk("hello timer\n");
30 return 0;
31 }
32
33 static void __exit test_exit(void) //模块卸载函数
34 {
35 del_timer(&my_timer);
//模块卸载时删除定时器
36 printk("good bye timer\n");
37 }
看效果:
[root: 2nd]# insmod test.ko
hello timer
[root: 2nd]# time out![99] [swapper] //五秒后打印第一句
time out![99] [swapper] //之后每两秒打印一次
time out![99] [swapper]
time out![99] [swapper]
time out![99] [swapper]
[root: 2nd]# rmmod test
good bye timer
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、利用定时器实现按键去抖
之前中断的时候曾经使用过写过按键的程序,不过那时候没有使用去抖,因为本来那个按键的硬件防抖就做得比较好。今天要选一个比较差的按键,来看看按键防抖的效果。
直接上程序:
/*th_time_2/3rd/test.c */
7 struct timer_list my_timer;
8
9 void timer_func(unsigned long data)
10 {
11 printk("key down\n");
12 }
13
14 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 }
21
22 int test_init(void)
23 {
24 int ret;
25 init_timer(&my_timer);
//初始化my_timer结构体
26 my_timer.function
= timer_func; //并告知结构体处理函数指针
27 /*注册中断,我的按键对应中断IRQ_EINT3*/
28 ret = request_irq(IRQ_EINT3, irq_handler, IRQF_TRIGGER_FALLING, "timer i rq", NULL);
29 /*注意一定要判断一下request_irq申请中断函数的返回值,来确认为什么进不了中断*/
30 if(ret)
31 {
32 printk("request irq failed\n");
33 return ret;
34 }
35
36 printk("hello kernel\n,[%d]", HZ);
37 return 0; 38 }
39 void test_exit(void)
40 {
41 free_irq(IRQ_EINT3, NULL); //注销中断
42 del_timer(&my_timer);
//注销内核中的struct
timer_struct
43 printk("bye\n");
44 }
38 }
39 void test_exit(void)
40 {
41 free_irq(IRQ_EINT3, NULL); //注销中断
42 del_timer(&my_timer);
//注销内核中的struct
timer_struct
43 printk("bye\n");
44 }
上面的程序可以看到,每次进入中断,都会刷新定时器的值。当按下按键时,由于抖动的关系,会出现多次的中断,但只有最后的一次中断才会触发一次定时器处理函数。
看效果:
[root: 3rd]# insmod test.ko
hello kernel
irq /按下一次按键,都由于抖动的关系执行了多次的中断处理函数
irq //每次的中断处理函数都会调用mod_tiemr更新定时器的时间
irq
irq
irq
irq
irq
irq
irq
irq
irq
irq
key down //只有最后一次执行完中断处理函数0.5s后,才会触发定时器,执行定时器处理函数
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、总结
这节介绍了如果使用定时器和如果通过定时器来实现按键去抖。
定时器的使用很简单,只需要三部:
1、定义定时器结构体timer_list。
2、设置超时时间,定义定时器处理函数和传参。
3、激活定时器。
另外还可以通过mod_timer和del_timer来修改或者删除定时器。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代码: 7th_time_2.rar
【Linux开发】linux设备驱动归纳总结(七):2.内核定时器的更多相关文章
- 【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时
linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(六):1.中断的实现
linux设备驱动归纳总结(六):1.中断的实现 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存
linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(四):5.多处理器下的竞态和并发
linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(四):1.进程管理的相关概念
linux设备驱动归纳总结(四):1.进程管理的相关概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(三):4.ioctl的实现
linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是 ...
- linux设备驱动归纳总结(七):1.时间管理与内核延时【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(十一):写个简单的看门狗驱动
linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(十二):简单的数码相框
linux设备驱动归纳总结(十二):简单的数码相框 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
随机推荐
- java常用检验接口control方式
@Controller public class ItemController { @Autowired private ItemService itemService; @RequestMappin ...
- 记使用vim常用的快捷键
显示行数 :set nu 跳转到最后一行 G 跳转到指定行数 nG n为行数 跳转到行首 0 跳转到行尾 $ (shift+4)
- [CSP-S模拟测试]:C(三分+贪心)
题目传送门(内部题46) 输入格式 第一行$3$个整数$n,m,t$.第二行$n$个整数,表示$P_i$.接下来$m$行每行两个整数,表示$L_i,R_i$. 输出格式 一行一个整数表示答案. 样例 ...
- [CSP-S模拟测试]:B(DP+数学)
题目传送门(内部题45) 输入格式 第一行$3$个整数$n,m,P$.第二行$m$个整数,表示$m$次询问. 输出格式 一行$m$个整数表示答案. 样例 样例输入1: 2 4 40 1 2 3 样例输 ...
- C++入门经典-例7.5-对象的指针,函数指针调用类成员
1:指向相应对象的指针就是对象的指针,它的生明方法与其他类型一样,如下: 类名 *p; 类的指针可以调用它所指向对象的成员.形式如下: p->类成员; 2:代码如下: (1)cat.h #inc ...
- Java多线程-线程中止
不正确的线程中止-Stop Stop:中止线程,并且清除监控器锁的信息,但是可能导致 线程安全问题,JDK不建议用. Destroy: JDK未实现该方法. /** * @author simon * ...
- echarts热力地图
<!DOCTYPE HTML> <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" ...
- How to Fix Grub error: no such partition Grub Rescue
错误信息: error: no such partition Entering rescue mode... grub rescue> _ 错误原因: grub找不到文件normal.mod 解 ...
- SpringBoot2.X 静态文件配置
Spring Boot 默认会挨个从 META/resources > resources > static > public 里面找是否存在相应的资源,如果有则直接返回. 默认配置 ...
- HearthBuddy卡组
偶数萨 手打两天已上传说,各位加油 欧洲牧羊人 ### 火元素换艾雅# 职业:萨满祭司# 模式:狂野模式## 2x (2) 图腾魔像 # 2x (2) 大漩涡传送门 # 2x (2 ...