【Linux开发】linux设备驱动归纳总结(七):2.内核定时器
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
這節(jié)將介紹內(nèi)核定時器的使用。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、定時器
之前說過兩類跟時間相關(guān)的內(nèi)核結(jié)構(gòu)。
1、延時:通過忙等待或者睡眠機(jī)制實現(xiàn)延時。
2、tasklet和工作隊列,通過某種機(jī)制使工作推后執(zhí)行,但不知道執(zhí)行的具體時間。
接下來要介紹的定時器,能夠使工作在指定的時間點(diǎn)上執(zhí)行,而且不需要使用忙等待這類的延時方法。通過定義一個定時器,告之內(nèi)核在哪個時間需要執(zhí)行什么函數(shù)就可以了,等時間一到,內(nèi)核會就執(zhí)行指定的函數(shù)。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、使用定時器
定時器的使用很簡單,只需要三部:
1、定義定時器結(jié)構(gòu)體timer_list。
2、設(shè)置超時時間,定義定時器處理函數(shù)和傳參。
3、激活定時器。
接下來一步步來說。
1、定義并初始化定時器結(jié)構(gòu)體timer_list。
/*include/linux/timer.h*/
11 struct timer_list {
12 struct list_head entry;
13 unsigned long expires; //設(shè)置在執(zhí)行定時器處理函數(shù)的時間
14
15 void (*function)(unsigned long); //定時器處理函數(shù)
16 unsigned long data; //處理函數(shù)的傳參
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 };
紅色部分是待會我們要自己賦值的,其他內(nèi)核幫忙搞定。
這個也是有靜態(tài)和動態(tài)區(qū)分,步驟如下:
靜態(tài)定義并初始化:
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
初始化結(jié)構(gòu)體的同時給指定測成員賦值
動態(tài)初始化:
/*定義一個名為my_timer的timer_list數(shù)據(jù)結(jié)構(gòu)*/
struct timer_list my_timer;
/*初始化my_timer的部分內(nèi)部成員*/
init_timer(&my_timer);
2、設(shè)置超時時間,定義定時器處理函數(shù)和傳參。
這一步驟是要填充timer_list的三個成員:expires、function和data。
三定時器處理函數(shù)的要求定義一個定時器處理函數(shù):
/*7th_time_2/1st/test.c*/
9 void timer_func(unsigned long data) //2.定義定時器處理函數(shù)
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.設(shè)定定時器處理函數(shù)觸發(fā)時間為5秒
19 my_timer.function = timer_func; //2.給結(jié)構(gòu)體指定定時器處理函數(shù)
20 my_timer.data = (unsigned long)99; //2.設(shè)定定時器處理函數(shù)的傳參
這里要注意一下,expires是指定定時器處理函數(shù)在什么時候觸發(fā),我這里定義在當(dāng)前時間(jiffies)的后5秒(5*HZ)。
3、激活定時器。
其實就一個函數(shù),一旦調(diào)用,內(nèi)核就會知道,在當(dāng)前時候的5秒后,執(zhí)行相應(yīng)的處理函數(shù)。
/*7th_time_2/1st/test.c*/
22 add_timer(&my_timer); //3.激活定時器
這里要注意,定時器激活后,它只會在指定時間執(zhí)行一次處理函數(shù),執(zhí)行后會將定時器在內(nèi)核中移除。
這樣就大功告成了,給個完整代碼:
/*7th_time_2/1st/test.c*/
1 #include
2 #include
3
4 #include
5 #include
6
7 struct timer_list my_timer; //1.定義定時器結(jié)構(gòu)體timer_list
8
9 void timer_func(unsigned long data) //2.定義定時器處理函數(shù)
10 {
11 printk("time out![%d] [%s]\n", (int)data,?current->comm); //打印當(dāng)前進(jìn)程
12 }
13
14 static int __init test_init(void) //模塊初始化函數(shù)
15 {
16 init_timer(&my_timer); //1.初始化timer_list結(jié)構(gòu)
17
18 my_timer.expires = jiffies + 5*HZ; //2.設(shè)定定時器處理函數(shù)觸發(fā)時間為5秒
19 my_timer.function = timer_func; //2.給結(jié)構(gòu)體指定定時器處理函數(shù)
20 my_timer.data = (unsigned long)99; //2.設(shè)定定時器處理函數(shù)的傳參
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) //模塊卸載函數(shù)
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
這里要注意的是,當(dāng)執(zhí)行處理函數(shù)是,當(dāng)前進(jìn)程是swapper,而不是加載模塊時的insmod,也就說明,當(dāng)調(diào)用add_timer后,就會將定時器交由內(nèi)核來管理,當(dāng)時間一到,內(nèi)核調(diào)用進(jìn)程swapper來執(zhí)行處理函數(shù)。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、定時器的刪除和修改
上面說了,激活定時器后只能執(zhí)行一遍,如果要實現(xiàn)隔指定時間又重復(fù)執(zhí)行,那就要修改一下代碼。
在定時器處理函數(shù)中加上兩條代碼:
/*7th_time_2/2nd/test.c*/
14 my_timer.expires = jiffies + 2*HZ; //重新設(shè)定時間,在兩秒后再執(zhí)行
15 add_timer(&my_timer); //再次激活定時器
這樣的話,每個2秒就會再次執(zhí)行定時器處理函數(shù)。
這兩條代碼也相當(dāng)與一下的函數(shù):
/*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)
這是改變定時器超時時間的函數(shù),如果在指定的定時器(timer)沒超時前調(diào)用,超時時間會更新為新的新的超時時間(expires)。如果在定時器超時后調(diào)用,那就相當(dāng)于重新指定超時時間并再次激活定時器。
如果想在定時器沒有超時前取消定時器,可以調(diào)用以下函數(shù):
/*kernel/timer.c*/
718 int del_timer(struct timer_list *timer)
該函數(shù)用來刪除還沒超時的定時器。
不貼個代碼:
/*7th_time_2/2nd/test.c*/
7 struct timer_list my_timer; //1.定義定時器結(jié)構(gòu)體timer_list
8
9 void timer_func(unsigned long data) //2.定義定時器處理函數(shù)
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相當(dāng)于14.15行兩步
18 }
19
20 static int __init test_init(void) //模塊初始化函數(shù)
21 {
22 init_timer(&my_timer); //1.初始化timer_list結(jié)構(gòu)
23
24 my_timer.expires = jiffies + 5*HZ; //2.設(shè)定定時器處理函數(shù)觸發(fā)時間為5秒
25 my_timer.function = timer_func; //2.給結(jié)構(gòu)體指定定時器處理函數(shù)
26 my_timer.data = (unsigned long)99; //2.設(shè)定定時器處理函
數(shù)的傳參
27
28 add_timer(&my_timer); //3.激活定時器
29 printk("hello timer\n");
30 return 0;
31 }
32
33 static void __exit test_exit(void) //模塊卸載函數(shù)
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
四、利用定時器實現(xiàn)按鍵去抖
之前中斷的時候曾經(jīng)使用過寫過按鍵的程序,不過那時候沒有使用去抖,因為本來那個按鍵的硬件防抖就做得比較好。今天要選一個比較差的按鍵,來看看按鍵防抖的效果。
直接上程序:
/*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秒觸發(fā)一次定時器處理函數(shù),則只有最后一次抖動的中斷會觸發(fā)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結(jié)構(gòu)體
26 my_timer.function = timer_func; //并告知結(jié)構(gòu)體處理函數(shù)指針
27 /*注冊中斷,我的按鍵對應(yīng)中斷IRQ_EINT3*/
28 ret = request_irq(IRQ_EINT3, irq_handler, IRQF_TRIGGER_FALLING, "timer i rq", NULL);
29 /*注意一定要判斷一下request_irq申請中斷函數(shù)的返回值,來確認(rèn)為什么進(jìn)不了中斷*/
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); //注銷內(nèi)核中的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); //注銷內(nèi)核中的struct timer_struct
43 printk("bye\n");
44 }
上面的程序可以看到,每次進(jìn)入中斷,都會刷新定時器的值。當(dāng)按下按鍵時,由于抖動的關(guān)系,會出現(xiàn)多次的中斷,但只有最后的一次中斷才會觸發(fā)一次定時器處理函數(shù)。
看效果:
[root: 3rd]# insmod test.ko
hello kernel
irq /按下一次按鍵,都由于抖動的關(guān)系執(zhí)行了多次的中斷處理函數(shù)
irq //每次的中斷處理函數(shù)都會調(diào)用mod_tiemr更新定時器的時間
irq
irq
irq
irq
irq
irq
irq
irq
irq
irq
key down //只有最后一次執(zhí)行完中斷處理函數(shù)0.5s后,才會觸發(fā)定時器,執(zhí)行定時器處理函數(shù)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、總結(jié)
這節(jié)介紹了如果使用定時器和如果通過定時器來實現(xiàn)按鍵去抖。
定時器的使用很簡單,只需要三部:
1、定義定時器結(jié)構(gòu)體timer_list。
2、設(shè)置超時時間,定義定時器處理函數(shù)和傳參。
3、激活定時器。
另外還可以通過mod_timer和del_timer來修改或者刪除定時器。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼:?7th_time_2.rar???
轉(zhuǎn)載于:https://www.cnblogs.com/huty/p/8518565.html
總結(jié)
以上是生活随笔為你收集整理的【Linux开发】linux设备驱动归纳总结(七):2.内核定时器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取系统昨日最高峰时的AWR报告,get
- 下一篇: 10分钟教你看懂mongodb的npm包