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.内核定时器的更多相关文章

  1. 【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时

    linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  2. 【Linux开发】linux设备驱动归纳总结(六):1.中断的实现

    linux设备驱动归纳总结(六):1.中断的实现 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  3. 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存

    linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  4. 【Linux开发】linux设备驱动归纳总结(四):5.多处理器下的竞态和并发

    linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  5. 【Linux开发】linux设备驱动归纳总结(四):1.进程管理的相关概念

    linux设备驱动归纳总结(四):1.进程管理的相关概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  6. 【Linux开发】linux设备驱动归纳总结(三):4.ioctl的实现

    linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是 ...

  7. linux设备驱动归纳总结(七):1.时间管理与内核延时【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ...

  8. 【Linux开发】linux设备驱动归纳总结(十一):写个简单的看门狗驱动

    linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 【Linux开发】linux设备驱动归纳总结(十二):简单的数码相框

    linux设备驱动归纳总结(十二):简单的数码相框 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. BZOJ 1984: 月下“毛景树” (树链剖分+线段树)

    注意赋值和加法的标记下传优先级.具体看代码. CODE #include <vector> #include <queue> #include <cstdio> # ...

  2. 【原】eclipse save action设置文件保存时自动格式化

    学了一段java了,发现其实eclipse有很多实用小功能,能够让你编码的时候快捷方便,如果你苦于一些你常用到的功能无法自动实现,那么你可以上网查一下,就会发现其实eclipse已经给你提供了便捷之路 ...

  3. yii ActiveRecord

    在活动记录里自定义属性(数据表里没有的属性), 起初没有注意到问题. 在这个继承了activeRecord的模型中, 还自定义了很多方法,  此为前提.  出现的问题是: 使用属性获取不到数据库的字段 ...

  4. 小程序开发之后台mybatis逆向工程(二)

    上一节搭建好了SSM后台框架,这一节将根据表结构创建实体及映射文件以及mapper接口.如果表过多,会很麻烦,所以mybatis提供了逆向工程来解决这个问题. 上一节 SSM搭建后台管理系统 逆向工程 ...

  5. 家庭问题x

    [问题描述] 有n个人,编号为1,2,……n,另外还知道存在K个关系.一个关系的表达为二元组(α,β)形式,表示α,β为同一家庭的成员. 当n,k和k个关系给出之后,求出其中共有多少个家庭.最大的家庭 ...

  6. vue使用子路由时,默认的子路由视图不显示问题

    解决办法是,将父级的name去掉.(大多数情况下是按name来跳转的,不过这样一改,调到父级就得用路径跳转了): 下面上一下路由的配置: { path: "/index", com ...

  7. JQuery属性操作之attr()和prop()的区别

    代码示例: <!doctype html> <html lang="en"> <head> <meta charset="UTF ...

  8. [CSP-S模拟测试]:那一天她里我而去(堆优化Dijkstra)

    题目传送门(内部题3) 输入格式 每个测试点有多组测试数据.第一行有一个正整数T表示数据组数.接下来对于每组数据,第一行有两个正整数n,m分别代表图的点数和边数.接下来有m行,每行三个整数u,v,d表 ...

  9. RabbitMQ MQTT协议和AMQP协议

    RabbitMQ MQTT协议和AMQP协议 1        序言... 1 1.1     RabbitMq结构... 1 1.2     RabbitMq消息接收... 4 1.3     Ex ...

  10. c++ gdb调试的基本使用

    https://blog.csdn.net/zdy0_2004/article/details/80102076