1.内核定时器:
Linux 内核所提供的用于操作定时器的数据结构和函数如下:
(1) timer_list
在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器
1 struct timer_list {
2 struct list_head entry; /* 定时器列表 */
3 unsigned long expires; /*定时器到期时间*/
4 void (*function)(unsigned long); /* 定时器处理函数 */
5 unsigned long data; /* 作为参数被传入定时器处理函数 */
6 struct timer_base_s *base;
7 ...
8 };
当定时器期满后,其中第 5 行的 function()成员将被执行,而第 4 行的 data 成员则是传入其中的参数,第 3 行的 expires 则是定时器到期的时间(jiffies)。
如下代码定义一个名为 my_timer 的定时器:
struct timer_list my_timer;
(2)初始化定时器
void init_timer(struct timer_list * timer);
上述 init_timer()函数初始化 timer_list 的 entry 的 next 为 NULL,并给 base 指针赋值
TIMER_INITIALIZER(_function, _expires, _data)宏用于赋值定时器结构体的function、expires、
data 和 base 成员
DEFINE_TIMER(_name, _function, _expires, _data)宏是定义并初始化定时器成员的“快捷方
式”。
此外,static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long),unsigned long data)
也可用于初始化定时器并赋值其成员
(3)增加定时器
void add_timer(struct timer_list* timer);
(4)删除定时器
int del_timer(struct timer_list* timer);
del_timer_sync()是 del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文。
(5).修改定时器的 expire
int mod_timer(struct timer_list *timer, unsigned long expires);
上述函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。
2.内核中延时的工作delayed_work
注意,对于这种周期性的任务,Linux 内核还提供了一套封装好的快捷机制,其本质利用工作队列
和定时器实现,这套快捷机制是就是delayed_work,delayed_work结构体的定义如代码清单10.11所示。
代码清单 10.11 delayed_work 结构体
1 struct delayed_work {
2 struct work_struct work;
3 struct timer_list timer;
4 };
5 struct work_struct {
6 atomic_long_t data;
7 #define WORK_STRUCT_PENDING 0
8 #define WORK_STRUCT_FLAG_MASK (3UL)
9 #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
10 struct list_head entry;
11 work_func_t func;
12 #ifdef CONFIG_LOCKDEP
13 struct lockdep_map lockdep_map;
14 #endif
15 };
我们可以通过如下函数调度一个 delayed_work 在指定的延时后执行:
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
当指定的 delay 到来时 delayed_work 结构体中 work 成员的 work_func_t 类型成员 func()会被
执行。work_func_t 类型定义为:
typedef void (*work_func_t)(struct work_struct *work);
其中 delay 参数的单位是 jiffies,因此一种常见的用法如下:
schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));
其中的 msecs_to_jiffies()用于将毫秒转化为 jiffies。
如果要周期性的执行任务,通常会在 delayed_work 的工作函数中再次调用 schedule_delayed_
work(),周而复始。
如下函数用来取消 delayed_work:
int cancel_delayed_work(struct delayed_work *work);
int cancel_delayed_work_sync(struct delayed_work *work);
3.内核延时
Linux 内核中提供了如下 3 个函数分别进行纳秒、微秒和毫秒延迟:
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。
毫秒时延(以及更大的秒时延)已经比较大了,在内核中,最好不要直接使用 mdelay()函数,这将无谓地耗费 CPU 资源,对于毫秒级以上时延,内核提供了下述函数:
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
上述函数将使得调用它的进程睡眠参数指定的时间,msleep()、ssleep()不能被打断,而msleep_interruptible()则可以被打断。
秒设备驱动程序:
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/platform_device.h> static unsigned char timermajor = 0; #define TIMERNAME "mytimer" static struct class *timer_class; static struct device *timer_device; struct timer_dev { struct cdev cdev; atomic_t counter; struct timer_list mytimer; }; struct timer_dev *my_timer_dev; static void my_timer_fun(unsigned int arg) { mod_timer(&my_timer_dev->mytimer, jiffies + HZ); atomic_inc(&my_timer_dev->counter); printk(KERN_NOTICE "current jiffies is %ld\n", jiffies); } static int my_timer_open(struct inode * inode, struct file * file) { init_timer( &my_timer_dev->mytimer); my_timer_dev->mytimer.expires = jiffies+ HZ; my_timer_dev->mytimer.function = & my_timer_fun; add_timer(&my_timer_dev->mytimer); atomic_set(&my_timer_dev->counter, 0); return 0; } static ssize_t my_timer_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { int counter; counter = atomic_read(&my_timer_dev->counter); if(copy_to_user(buf, &counter, sizeof(int))) printk("copy_to_user error\n"); return 0; } static int my_timer_close(struct inode *inode, struct file *file) { del_timer( &my_timer_dev->mytimer); return 0; } struct file_operations timer_fops = { .owner = THIS_MODULE, .open = my_timer_open, .read = my_timer_read, .release = my_timer_close, }; static void timer_setup_cdev(struct timer_dev *dev, int minor ) { unsigned char err; dev_t deno; deno = MKDEV(timermajor, minor); cdev_init(&dev->cdev, &timer_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &timer_fops; err = cdev_add(&dev->cdev, deno, 1); if(err) printk(KERN_ALERT"cdev_addd error\n"); } static int __init my_timer_init(void) { int err = 0; dev_t deno; if(timermajor) { register_chrdev_region(deno, 1, TIMERNAME); } else { err = alloc_chrdev_region(&deno, 0, 1, TIMERNAME); timermajor = MAJOR(deno); } if(err) printk(KERN_ALERT"alloc_chrdev_region error\n"); printk(KERN_ALERT"timermajor is %d\n", timermajor); my_timer_dev = kmalloc(sizeof(struct timer_dev), GFP_KERNEL); if(!my_timer_dev) { printk(KERN_ALERT"kmalloc error\n"); return -ENOMEM; } memset(my_timer_dev, 0, sizeof(struct timer_dev) ); timer_setup_cdev(my_timer_dev, 0); timer_class = class_create(THIS_MODULE, TIMERNAME); if(IS_ERR(timer_class)) { printk(KERN_ALERT"class_create error\n"); return -EBUSY; } timer_device = device_create(timer_class, NULL, deno, NULL, TIMERNAME); if(IS_ERR(timer_device)) { printk(KERN_ALERT"device_create error\n"); return -EBUSY; } return 0; } static void __exit my_key_exit(void) { cdev_del(&my_timer_dev->cdev); unregister_chrdev_region(MKDEV(timermajor, 0), 1); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("qigaohua"); module_init(my_timer_init); module_exit(my_key_exit);
测试程序:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { int fd; int counter = 0; int old_counter = 0; fd = open("/dev/mytimer", O_RDWR); if(fd < 0) { printf("open /dev/mytimer error\n"); return 0; } while(1) { read(fd, &counter, sizeof(int)); if(counter != old_counter) { old_counter = counter; printf("counter is %d\n", counter); } } }