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");
內核定時器應用案例: 用來做按鍵消除抖動
按鍵中斷雙邊觸發 --- 只要處於抖動階段,就會頻繁進出中斷程序
如果在中斷程序中修改定時器到期時間,這樣只要處於抖動階段,定時器就不會有超時出現。當電平穩定後,不會再進入中斷程序,這樣定時器就有機會超時,然後在執行定時處理函數,只需要把原來在按鍵中做事件搬運裏面就可以。