在linux中如果想周期性的干一些事情,或在固定某个时间干一件事情,可以使用内核定时器来完成。
比如:按键扫描等 。
下面先看内核定时器的数据组成。
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires; /* 定时器唤醒时间 */
struct tvec_base *base;
void (*function)(unsigned long); /* 唤醒后要做的事 */
unsigned long data;
int slack;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
其中我们主要来填充唤醒时间和,和醒后要做的事。其它参数初始化可以使用系统提供的固定的定时器初始化函数。
接下来看一下初始化函数。
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
/**
* init_timer_key - initialize a timer
* @timer: the timer to be initialized
* @flags: timer flags
* @name: name of the timer
* @key: lockdep class key of the fake lock used for tracking timer
* sync lock dependencies
*
* init_timer_key() must be done to a timer prior calling *any* of the
* other timer functions.
*/
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
debug_init(timer);
do_init_timer(timer, flags, name, key);
}
具体的初始化do_init_timer。因为它里面就初始化了一些通用性的参数。具体的那两个还需要我们手动初始化。
static void do_init_timer(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
struct tvec_base *base = __raw_get_cpu_var(tvec_bases);
timer->entry.next = NULL; /* 初始化链表节点 */
timer->base = (void *)((unsigned long)base | flags);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
带初始化完成后,就需要使用下面这个函数把初始化的参数加入到定时器链表中去。
这里要注意的是内核定时器是一个单次的定时器(以一个时间点定时就可以看出来)。
/**
* add_timer - start a timer
* @timer: the timer to be added
*
* The kernel will do a ->function(->data) callback from the
* timer interrupt at the ->expires point in the future. The
* current time is 'jiffies'.
*
* The timer's ->expires, ->function (and if the handler uses it, ->data)
* fields must be set prior calling this function.
*
* Timers with an ->expires field in the past will be executed in the next
* timer tick.
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer)); /* 检查该timer有没有被挂起,或*/
mod_timer(timer, timer->expires); /* 没有被挂起,则修改定时器的到期时间 */
}
可以看到mod_timer函数还是调用的mod_timer函数来加入定时器链表中的。
判断有没有被挂起,用下面函数,很简单。
/**
* timer_pending - is a timer pending?
* @timer: the timer in question
* 用来判断一个处在定时器管理队列中的定时器对象是否已经被调度执行
* timer_pending will tell whether a given timer is currently pending,
* or not. Callers must ensure serialization wrt. other operations done
* to this timer, eg. interrupt contexts, or other CPUs on SMP.
*
* return value: 1 if the timer is pending, 0 if not.
*/
static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL; /* 该值默认初始化是被初始化为NULL的 */
}
修改(更新)定时器的定时时间则是用下面这个函数。主要用来重复定时。
/**
* mod_timer - modify a timer's timeout
* @timer: the timer to be modified
* @expires: new timeout in jiffies
*
* mod_timer() is a more efficient way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
*
* mod_timer(timer, expires) is equivalent to:
*
* del_timer(timer); timer->expires = expires; add_timer(timer);
*
* Note that if there are multiple unserialized concurrent users of the
* same timer, then mod_timer() is the only safe way to modify the timeout,
* since add_timer() cannot modify an already running timer.
*
* The function returns whether it has modified a pending timer or not.
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.)
*/
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_pending(timer) && timer->expires == expires) /* 检测有效性 */
return 1;
expires = apply_slack(timer, expires);
return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
删除一个定时器。
/**
* del_timer - deactive a timer.
* @timer: the timer to be deactivated
*
* del_timer() deactivates a timer - this works on both active and inactive
* timers.
*
* The function returns whether it has deactivated a pending timer or not.
* (ie. del_timer() of an inactive timer returns 0, del_timer() of an
* active timer returns 1.)
*/
int del_timer(struct timer_list *timer)
{
struct tvec_base *base;
unsigned long flags;
int ret = 0;
timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) { /* 判断是否已经被执行inactive,return0 */
base = lock_timer_base(timer, &flags);
if (timer_pending(timer)) {
detach_timer(timer, 1); /* 在这个函数中删除还没被执行的active ,return 1 */
if (timer->expires == base->next_timer &&
!tbase_get_deferrable(timer->base))
base->next_timer = base->timer_jiffies;
ret = 1;
}
spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
下面是驱动中使用内核定时器的一个简单范例。
#include
/* 定义一个定时器指针 */
static struct timer_list *timer;
/* 参数是timer中的变量data */
void function_handle(unsigned long data)
{
/* 做你想做的事 */
......
/* 因为内核定时器是一个单次的定时器,所以如果想要多次重复定时需要在定时器绑定的函数结尾重新装载时间,并启动定时 */
/* Kernel Timer restart */
mod_timer(timer,jiffies + HZ/50);
}
int xxxx_init(void)
{
timer = kzalloc(sizeof(struct timer_list), GFP_KERNEL);
if (!timer )
{
/* 错误处理 */
}
/* 具体任务的注册等 */
......
init_timer(timer ); /* 初始化定时器 */
timer->function = function_handle; /* 绑定定时时间到后的执行函数 */
timer->expires = jiffies + (HZ/50); /* 定时的时间点,当前时间的20ms之后 */
add_timer(timer); /* 添加并启动定时器 */
}
void xxxx_exit(void)
{
/* 具体任务的卸载等 */
......
/* 删除定时器 */
del_timer(timer);
}
module_init(xxxx_init);
module_exit(xxxx_exit);
MODULE_LICENSE("GPL");