linux內核中延遲執行的機制之內核定時器介紹

linux 內核定時器、linux 內核共享工作隊列、linux 內核tasklet機制 

以上的3中方法都可以吧工作推後執行。下面介紹linux內核定時器:


內核定時器本質是使用一個硬件定時器實現。相當於裸機程序編寫的一個定時器中斷程序,在中斷程序中有一個變量,每進一次中斷增加1 。上面所說全局就是就是  jiffies ,這個值從開機開始,一直在變量 。

內核定時器定時使用一個結構體表示 :

Timer.h (include\linux)    

struct timer_list {
    struct list_head entry;                        //定時鏈表,不需要關心
    unsigned long expires;                         //到期時間 
    void (*function)(unsigned long data);          //一個函數指針,也就是定時時間 到會執行函數
    unsigned long data;                            //傳遞給上面函數指針調用時的實際參數
    
    struct tvec_base *base;                        //不需要關心
   //以下成員不需要關心
    int slack;
    
#ifdef CONFIG_TIMER_STATS
    int start_pid;
    void *start_site;
    char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

 一個這個結構就表示一個時間 定時。

expires:  是一個未來的時間點。使用系統時鐘節拍做單位(也就是上面的說硬件定時器,一次進入中斷的時間)。這個值設定應該使用  jiffies 變量 + 你要定時的時間(轉換成時鐘節拍)。定時時間可以使用 HZ  * 秒 ,如果 是ms ,使用 unsigned long msecs_to_jiffies(const unsigned int m) 函數進行轉換。

function:定時時間 到執行的函數。原型    void (*function)(unsigned long data);

data: 當    function 函數執行,需要傳遞實際參數,傳遞就是data 

相關的API函數:

1)靜態方式:初始化timer_list 結構

#define TIMER_INITIALIZER(_function, _expires, _data) {     \
        .entry = { .prev = TIMER_ENTRY_STATIC },    \
        .function = (_function),            \
        .expires = (_expires),              \
        .data = (_data),                \
        .base = &boot_tvec_bases,           \
        .slack = -1,                    \
        __TIMER_LOCKDEP_MAP_INITIALIZER(        \
            __FILE__ ":" __stringify(__LINE__)) \
    }

示例:

struct timer_list  timer =  TIMER_INITIALIZER(myfunction, jiffies+HZ, 100);

2)動態初始化

init_timer(timer)    :  使用時候是一個指針。

初始化  struct timer_list 結構,初始化後需要自己去填充 function,expires,data成員 。

示例:

struct timer_list  timer;  --全局變量

在適合的函數內:

init_timer(&timer)  ;
timer.function = myfunction;
timer.expire = jiffies+HZ;
timer.data = 100;

或使用 宏 setup_timer(timer, fn, data)   :timer傳遞的也是一個指針

示例:以下兩句等效於上面的4條代碼

timer.expire = jiffies+HZ;
setup_timer(&timer, myfunction, 99) ;

3)添加定時器到定時器鏈表中

void add_timer(struct timer_list *timer);

功能:把timer結構添加到定時器鏈表上,以及激活定時器,添加後會開始計時,定時到就執行結構中的function指針所指向的函數。

參數:timer 已經初始化好的 struct timer_list 結構指針。

4)刪除定時鏈表上指定的 struct timer_list 結構。

int del_timer(struct timer_list *timer)

功能:刪除後,對應的 timer_list 不會再執行。這個在單核心CPU中使用。在多核心CPU使用中不安全。

int del_timer_sync(struct timer_list *timer)

功能上del_timer(struct timer_list *timer)    相同,只是它可以多核心CPU安全運行。

5) 修改已經添加到定時鏈表中的 struct timer_list 結構中的到期時間 

int    mod_timer(struct timer_list * timer,unsigned long expires)

功能:1)修改  expires 時間 ; 2)如果所修改的結構沒有使用add_timer 添加,修改會加進去。定時從此開始。

要定時一段時間步驟:

1)定義一個 timer_list 結構
2)初始化 timer_list 結構
3)添加 timer_list到定時鏈表中
4)編寫定時處理函數
5)刪除timer_list結構

要實現循環定時--- 直接在定時處理函數中重新修改 到期時間。

示例:

#include <linux/module.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/timer.h>
void timer_func(unsigned long data)	;

//靜態初始化
struct timer_list   my_timer = TIMER_INITIALIZER(timer_func,0,(unsigned long)99);			    //1.定義定時器結構體timer_list

//動態初始化  
//struct timer_list my_timer;

void timer_func(unsigned long data)		//2.定義定時器處理函數
{
	  printk(KERN_EMERG"time out![%d] [%s]\n", (int)data, current->comm);
       my_timer.expires = jiffies + 5 * HZ;//2.設定定時器處理函數觸發時間爲5秒
        add_timer(&my_timer);				        //3.激活定時器
}


static int __init test_init(void)	    //模塊初始化函數
{
	/*
	init_timer(&my_timer);				      //1.初始化timer_list結構                       
	my_timer.expires = jiffies + 5 * HZ;//2.設定定時器處理函數觸發時間爲5秒
	my_timer.function = timer_func;		  //2.給結構體指定定時器處理函數
	my_timer.data = (unsigned long)99;	//2.設定定時器處理函數的傳參
   */
	//或:
	//my_timer.expires = jiffies + 2 * HZ;//2.設定定時器處理函數觸發時間爲2秒	
	//setup_timer(&my_timer, timer_func, (unsigned long)99);
	
	add_timer(&my_timer);				        //3.激活定時器
	
	printk(KERN_EMERG"hello timer,current->comm[%s]\n", current->comm);
	return 0;
}

static void __exit test_exit(void)		//模塊卸載函數
{
	//del_timer(&my_timer);				        //3.卸載定時器	
	del_timer_sync(&my_timer);		        //3.卸載定時器(多核CPU要使用這個)
	printk("good bye timer\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xoao bai");
MODULE_VERSION("v0.1");

內核定時器應用案例: 用來做按鍵消除抖動

按鍵中斷雙邊觸發  ---  只要處於抖動階段,就會頻繁進出中斷程序
如果在中斷程序中修改定時器到期時間,這樣只要處於抖動階段,定時器就不會有超時出現。當電平穩定後,不會再進入中斷程序,這樣定時器就有機會超時,然後在執行定時處理函數,只需要把原來在按鍵中做事件搬運裏面就可以。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章