struct timer_list { struct list_head entry; //定时器列表 unsigned long expires; //定时器到期时间 void (*function)(unsigned long); //定时器处理函数 unsigned long data; //作为参数被传入定时器处理函数 struct timer_base_s *base; };当定时器期满后, 其中第 5 行的 function()成员将被执行, 而第 4 行的 data 成员则是传入其中的参数,第 3 行的 expires 则是定时器到期的时间(jiffies)。
如下代码定义一个名为 my_timer 的定时器:
struct timer_list my_timer;
2.初始化定时器static inline void setup_timer(struct timer_list * timer, void (*function)(unsignedlong), unsigned long data) { timer->function = function; timer->data = data; init_timer(timer); }3.增加定时器
上述函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。
代码清单 10.10 给出了一个完整的内核定时器使用模板,大多数情况下,设备驱动都如这个模板那样使用定时器。
代码清单 10.10 内核定时器使用模板
/*xxx 设备结体*/ struct xxx_dev { struct cdev cdev; ... timer_list xxx_timer;/*设备要使用的定时器*/ }; /*xxx 驱动中的某函数*/ xxx_func1(...) { 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_func2(…) { ... /*删除定时器*/ 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); ... }从代码清单第 19、 40 行可以看出, 定时器的到期时间往往是在目前 jiffies 的基础上添加一个时延,若为 Hz,则表示延迟 1s。在定时器处理函数中,在做完相应的工作后,往往会延后 expires 并将定时器再次添加到内核定时器链表,以便定时器能再次被触发。
10.5.2 实例:秒字符设备
下面我们编写一个字符设备“second” (即“秒” )的驱动,它在被打开的时候初始化一个定时器并将其添加到内核定时器链表,每秒输出一次当前的 jiffies(为此,定时器处理函数中每次都要修改新的 expires),整个程序如代码清单 10.11。
代码清单 10.1 1 使用内核定时器的 second 字符设备驱动
#include ... #define SECOND_MAJOR 252 /*预设的 second 的主设备号*/ static int second_major = SECOND_MAJOR; /*second 设备结体*/ struct second_dev { struct cdev cdev; /*cdev 结体*/ atomic_t counter;/* 一共经历了多少秒?*/ struct timer_lists_timer; /*设备要使用的定时器*/ }; struct second_dev *second_devp; /*设备结构体指针*/ /*定时器处理函数*/ static void second_timer_handle(unsigned long arg) { mod_timer(&second_devp->s_timer,jiffies + HZ); atomic_inc(&second_devp->counter); printk(KERN_NOTICE "current jiffies is %ld\n", jiffies); } /*文件打开函数*/ int second_open(struct inode *inode, struct file *filp) { /*初始化定时器*/ init_timer(&second_devp->s_timer); second_devp->s_timer.function = &second_timer_handle; second_devp->s_timer.expires = jiffies +HZ; add_timer(&second_devp->s_timer); /*添加(注册)定时器*/ atomic_set(&second_devp->counter,0); //计数清零 return 0; } /*文件释放函数*/ int second_release(struct inode *inode,struct file *filp) { del_timer(&second_devp->s_timer); return 0; } /*globalfifo 读函数*/ static ssize_t second_read(struct file *filp, char _ _user *buf, size_t count, loff_t *ppos) { int counter; counter = atomic_read(&second_devp->counter); if(put_user(counter, (int*)buf)) return - EFAULT; else return sizeof(unsigned int); } /*文件操作结体*/ static const struct file_operations second_fops = { .owner = THIS_MODULE, .open = second_open, .release = second_release, .read = second_read, }; /*初始化并注册 cdev*/ static void second_setup_cdev(struct second_dev *dev, int index) { int err, devno = MKDEV(second_major, index); cdev_init(&dev->cdev, &second_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &second_fops; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding LED%d", err, index); } /*设备驱动模块加载函数*/ int second_init(void) { int ret; dev_t devno = MKDEV(second_major, 0); /* 申请设备号*/ if (second_major) ret = register_chrdev_region(devno, 1,"second"); else /* 动态申请设备号 */ { ret = alloc_chrdev_region(&devno, 0,1, "second"); second_major = MAJOR(devno); } if (ret < 0) return ret; /* 动态申请设备结体的内存*/ second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL); if (!second_devp) /*申请失败*/ { ret = - ENOMEM; goto fail_malloc; } memset(second_devp, 0, sizeof(struct second_dev)); second_setup_cdev(second_devp, 0); return 0; fail_malloc: unregister_chrdev_region(devno, 1); } /*模块卸载函数*/ void second_exit(void) { cdev_del(&second_devp->cdev); /*注销 cdev*/ kfree(second_devp); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(second_major, 0), 1); /*释放设备号*/ } MODULE_AUTHOR("Song Baohua"); MODULE_LICENSE("Dual BSD/GPL"); module_param(second_major, int,S_IRUGO); module_init(second_init); module_exit(second_exit);
在 second 的 open()函数中,将启动定时器,此后每 1s 会再次运行定时器处理函数,在 second 的 release()函数中,定时器被删除。
second_dev 结构体中的原子变量 counter 用于秒计数,每次在定时器处理函数中将被 atomic_inc()调用原子的增 1,second 的 read()函数会将这个值返回给用户空间。