软件意义上的定时器最终以来硬件定时器来实现,内核在时钟中断发生后执行检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。实质上,时钟中断处理程序会唤起TIMER——SOFTIRQ软中断,运行当前处理器上到期的所有定时器。
在LINUX驱动编程中,可以利用Linux内核中提供的一组函数和数据结构来完成定时触发工作或完成某周期性的事务。
time_list结构体的一个实例对应一个定时器。
struct timer_list {
struct list_head entry; /* 定时器列表 */
unsigned long expires; /* 定时器到期时间 */
void (*function)(unsigned long); /* 定时器处理函数 */
unsigned long data; /* 作为参数被传入定时器处理函数 */
struct timer_base_s *base;
...
};
内核定时器使用模板:
/* xxx设备结构体 */
struct xxx_dev {
struct cdev cdev;
...
timer_list xxx_timer; /* 设备要使用的定时器 */
};
/* xxx驱动中的某函数 */
xxx_funcl(...)
{
struct xxx_dev *dev = filp->private_data;
...
/* 初始化定时器 */
init_timer(&dev->xxx_timer);
dev->xxx_timer.function = &xxx_do_timer;
dev->xxx_timer.data = (unsigned long)dev; /* 设备结构体指针作为定时器处理函数参数 */
dev->xxx_timer.expires = jiffies + delay;
/* 添加(注册)定时器 */
add_timer(&dev->xxx_timer);
...
}
/* xxx驱动中的某函数 */
xxx_fun2(...)
{
....
/* 删除定时器 */
del_timer(&dev->xxx_timer);
...
}
/* 定时器处理函数 */
static void xxx_do_timer(unsigned long arg)
{
struct xxx_device *dev = (struct xxx_device *)(arg);
...
dev->xxx_timer.expires = jiffies + delay;
add_timer(&dev->xxx_timer);
...
}
定时器的到期时间往往是在目前jiffies的基础上添加一个时延,若为HZ,则表示1s.
在定时器处理函数中,在做完相应的工作后,往往会延后expires并将定时器再次添加到内核定时器链表,以便能再次被触发。