日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

【Linux开发】linux设备驱动归纳总结(七):2.内核定时器

發(fā)布時間:2025/7/14 linux 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux开发】linux设备驱动归纳总结(七):2.内核定时器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
linux設(shè)備驅(qū)動歸納總結(jié)(七):2.內(nèi)核定時器


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

這節(jié)將介紹內(nèi)核定時器的使用。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


一、定時器


之前說過兩類跟時間相關(guān)的內(nèi)核結(jié)構(gòu)。

1、延時:通過忙等待或者睡眠機制實現(xiàn)延時。

2tasklet和工作隊列,通過某種機制使工作推后執(zhí)行,但不知道執(zhí)行的具體時間。

接下來要介紹的定時器,能夠使工作在指定的時間點上執(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_timertimer_list數(shù)據(jù)結(jié)構(gòu)*/

struct timer_list my_timer;

/*初始化my_timer的部分內(nèi)部成員*/

init_timer(&my_timer);


2、設(shè)置超時時間,定義定時器處理函數(shù)和傳參。

這一步驟是要填充timer_list的三個成員:expiresfunctiondata

三定時器處理函數(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ā),我這里定義在當前時間(jiffies)的后5秒(5*HZ)。


3、激活定時器。


其實就一個函數(shù),一旦調(diào)用,內(nèi)核就會知道,在當前時候的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); //打印當前進程

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

這里要注意的是,當執(zhí)行處理函數(shù)是,當前進程是swapper,而不是加載模塊時的insmod,也就說明,當調(diào)用add_timer后,就會將定時器交由內(nèi)核來管理,當時間一到,內(nèi)核調(diào)用進程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ù)。

這兩條代碼也相當與一下的函數(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)用,那就相當于重新指定超時時間并再次激活定時器。


如果想在定時器沒有超時前取消定時器,可以調(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相當于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ù)的返回值,來確認為什么進不了中斷*/

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 }

上面的程序可以看到,每次進入中斷,都會刷新定時器的值。當按下按鍵時,由于抖動的關(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_timerdel_timer來修改或者刪除定時器。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代碼:?7th_time_2.rar???

轉(zhuǎn)載于:https://www.cnblogs.com/huty/p/8518565.html

總結(jié)

以上是生活随笔為你收集整理的【Linux开发】linux设备驱动归纳总结(七):2.内核定时器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。