【Linux开发】linux设备驱动归纳总结(七):2.内核定时器
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
這節將介紹內核定時器的使用。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、定時器
之前說過兩類跟時間相關的內核結構。
1、延時:通過忙等待或者睡眠機制實現延時。
2、tasklet和工作隊列,通過某種機制使工作推后執行,但不知道執行的具體時間。
接下來要介紹的定時器,能夠使工作在指定的時間點上執行,而且不需要使用忙等待這類的延時方法。通過定義一個定時器,告之內核在哪個時間需要執行什么函數就可以了,等時間一到,內核會就執行指定的函數。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、使用定時器
定時器的使用很簡單,只需要三部:
1、定義定時器結構體timer_list。
2、設置超時時間,定義定時器處理函數和傳參。
3、激活定時器。
接下來一步步來說。
1、定義并初始化定時器結構體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);
2、設置超時時間,定義定時器處理函數和傳參。
這一步驟是要填充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.設定定時器處理函數觸發時間為5秒
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???
轉載于:https://www.cnblogs.com/huty/p/8518565.html
總結
以上是生活随笔為你收集整理的【Linux开发】linux设备驱动归纳总结(七):2.内核定时器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取系统昨日最高峰时的AWR报告,get
- 下一篇: 10分钟教你看懂mongodb的npm包