在内核中,如果我们想在将来的某个时间点调度执行某个动作,同时在该时间点到达之前不会阻塞当前进程,那么内核定时器是个不错的选择。例如,一些周期性的操作等等。
那么到底什么是定时器呢,要实现一个定时器,都需要哪些操作?
内核定时器是一个数据结构,它告诉内核在一个用户定义的时间点,使用用户定义的参数来执行一个用户定义的函数。其数据结构是这样的:
#include
struct timer_list {
struct list_head entry;
unsigned long expires;
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和工作队列,参见
阅读(934) | 评论(0) | 转发(0) |