在核心中,如果我們想在将來的某個時間點排程執行某個動作,同時在該時間點到達之前不會阻塞目前程序,那麼核心定時器是個不錯的選擇。例如,一些周期性的操作等等。
那麼到底什麼是定時器呢,要實作一個定時器,都需要哪些操作?
核心定時器是一個資料結構,它告訴核心在一個使用者定義的時間點,使用使用者定義的參數來執行一個使用者定義的函數。其資料結構是這樣的:
#include
struct timer_list {
struct list_head entry;//核心使用
unsigned long expires;//逾時的jiffies值
void (*function)(unsigned long);//逾時處理函數
unsigned long data;//逾時處理函數參數
struct tvec_t_base_s *base;//核心使用
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
};
其中,expires是表示定時器執行是的時間值(即jiffies值);在到達該jiffies時間的時候,函數function将被調用,并傳遞data參數。
當然,如果要傳遞的參數不止一個的時候,可以将參數捆綁成一個資料結構,然後将該資料結構的指針強制轉換成unsigned long傳入。
定時器timer的基本操作也比較直覺。可能讀者已經猜到一二了,初始化、加入到定時器連結清單、在必要的時候(比如解除安裝子產品)delet掉就行了。
1. 初始化時應該首先調用init_timer初始化除expires、function、data之外的參數,然後再調用宏TIMER_INITIALIZER來初始化這三個參數:
void fastcall init_timer(struct timer_list *timer)
{
timer->entry.next = NULL;
timer->base = __raw_get_cpu_var(tvec_bases);
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
}
#define TIMER_INITIALIZER(_function, _expires, _data) { \
.function = (_function), \
.expires = (_expires), \
.data = (_data), \
.base = &boot_tvec_bases, \
}
另外一個宏DEFINE_TIMER也可以用來初始化
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
2. 加載定時器
extern void add_timer(struct timer_list *timer);
3. 删除定時器
extern int del_timer(struct timer_list * timer);
當然,針對定時器還有一些其他的函數:
extern int mod_timer(struct timer_list *timer, unsigned long expires);更新一個定時器的到期時間
extern void add_timer_on(struct timer_list *timer, int cpu);将定時器加載到指定的cpu上,用于對稱多處理器(smp)
static inline int timer_pending(const struct timer_list * timer)傳回定時器是否正在被排程運作
還有很多函數,具體參見linux/timer.h
下面給出一個執行個體,在2.6.24核心測試通過:
#include
#include
#include
#include
#include
static void timer_handler (unsigned long data)
{
printk(KERN_ALERT "The jiffies in timer_handler is %lu\n",jiffies);
printk(KERN_ALERT "timer_handler is running.\n");
}
DEFINE_TIMER(my_timer,timer_handler,HZ,0);
static int __init test_init(void)
{
printk(KERN_ALERT "The jiffies is %lu\n",jiffies);
add_timer(&my_timer);
return 0;
}
static void __exit test_exit(void)
{
del_timer(&my_timer);
printk(KERN_ALERT "test_exit is running.\n");
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);
測試結果:
The jiffies is 2425862
The jiffies in timer_handler is 2425863
timer_handler is running.
test_exit is running.
最後,還有幾點關于核心定時器的說明:
- 核心定時器通常作為“軟中斷”在中斷上下文執行,是以不能通路使用者空間、此時current指針沒有任何意義、不能排程和休眠。可用in_interrupt()來判斷是否正在運作在中斷上下文。
- 在SMP系統中,定時器函數會由注冊它的同一CPU執行。
- 核心定時器也可以将自己注冊以在稍後的時間重新運作。
- 定時器也可能是競态的來源,也就是說定時器也可能導緻并發與競争。
其實中斷中的tasklet機制很類似于核心定時器。
關于tasklet和工作隊列,參見
http://www.kerneltravel.net/?p=143
http://www.kerneltravel.net/?p=257