linux驅(qū)動之內(nèi)核定時器驅(qū)動設計
Fedora 14 內(nèi)核版本為2.6.38.1
開發(fā)板:ARM9 TQ2440
移植內(nèi)核版本:linux-2.6.30.4
定時器在linux內(nèi)核中主要是采用一個結構體實現(xiàn)的。但是需要注意定時器是一個只運行一次的對象,也就是當一個定時器結束以后,還需要重現(xiàn)添加定時器。但是可以采用mod_timer()函數(shù)動態(tài)的改變定時器到達時間。
這個驅(qū)動主要實現(xiàn)內(nèi)核定時器的基本操作。內(nèi)核定時器主要是是通過下面的結構體struct timer_list實現(xiàn)。需要的頭文件包括#include,但是在實際開發(fā)過程中不需要包含該頭文件,因為在sched.h中包含了該頭文件。
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
定時器的實現(xiàn)主要是該結構體的填充和部分函數(shù)的配合即可完成。其中紅色的部分是最主要的幾個元素,1、expires主要是用來定義定時器到期的時間,通常采用jiffies這個全局變量和HZ這個全局變量配合設置該元素的值。比如expires = jiffies + n*HZ,其中jiffies是自啟動以來的滴答數(shù),HZ是一秒種的滴答數(shù)。
2、function可以知道是一個函數(shù)指針,該函數(shù)就是定時器的處理函數(shù),類似我們在中斷中的中斷函數(shù),其實定時器和中斷有很大的相似性。定時器處理函數(shù)是自己定義的函數(shù)。
3、data通常是實現(xiàn)參數(shù)的傳遞,從function的參數(shù)類型可以知道,data可以作為定時器處理函數(shù)的參數(shù)。
其他的元素可以通過內(nèi)核的函數(shù)來初始化。
初始化函數(shù)為:
init_timer(struct timer_list * timer);
或者直接DEFINE_TIMER宏實現(xiàn)定義和初始化操作。
#define DEFINE_TIMER(_name, _function, _expires, _data)
struct timer_list _name =
TIMER_INITIALIZER(_function, _expires, _data)
添加定時器到內(nèi)核的函數(shù):
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
刪除定時器函數(shù),如果定時器的定時時間還沒有到達,那么才可以刪除定時器:
int del_timer(struct timer_list *timer)
修改定時器的到達時間,該函數(shù)的特點是,不管定時器是否到達時間,都會重現(xiàn)添加一個定時器到內(nèi)核。所以可以在定時處理函數(shù)中可以調(diào)用該函數(shù)修改需要重新定義的到達時間。
int mode_timer(struct timer_list *timer,unsigned long expires)
int mod_timer(struct timer_list *timer, unsigned long expires)
{
/*
* This is a common optimization triggered by the
* networking code - if the timer is re-modified
* to be the same thing then just return:
*/
if (timer->expires == expires && timer_pending(timer))
return 1;
/*注意調(diào)用的條件,也就是說明當前的定時器為鏈表的最后一個*/
return __mod_timer(timer, expires, false);
}
static inline int
__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
{
struct tvec_base *base, *new_base;
unsigned long flags;
int ret;
ret = 0;
timer_stats_timer_set_start_info(timer);
BUG_ON(!timer->function);
base = lock_timer_base(timer, &flags);
if (timer_pending(timer)) {
detach_timer(timer, 0);
ret = 1;
} else {
if (pending_only)
goto out_unlock;
}
debug_timer_activate(timer);
new_base = __get_cpu_var(tvec_bases);
if (base != new_base) {
/*
* We are trying to schedule the timer on the local CPU.
* However we cant change timers base while it is running,
* otherwise del_timer_sync() cant detect that the timers
* handler yet has not finished. This also guarantees that
* the timer is serialized wrt itself.
*/
if (likely(base->running_timer != timer)) {
/* See the comment in lock_timer_base() */
timer_set_base(timer, NULL);
spin_unlock(&base->lock);
base = new_base;
spin_lock(&base->lock);
timer_set_base(timer, base);
}
}
timer->expires = expires;
internal_add_timer(base, timer);
out_unlock:
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies;
struct list_head *vec;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} else if ((signed long) idx < 0) {
/*
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
} else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
/*
* Timers are FIFO:
*/
/*添加到鏈表的最后,這說明mod_timer實現(xiàn)了重新注冊一個定時器的操作*/
list_add_tail(&timer->entry, vec);
}
從上面的分析可以看出,mod_timer的實現(xiàn)過程比較復雜,但是基本上說明了mod_timer函數(shù)重新注冊定時器的操作過程。
一般而言定時器的基本操作主要是上面的幾個函數(shù)。
評論