本文简述如何使用linux里的定时器,包括周期性中断,停止和启动的控制。
日常常用的几个定时器相关linux API函数:
- init_timer(); 初始化定时器
- add_timer(); 启动定制器
- del_timer();停止定时器
- mod_timer(); 重新修改定时器当前计数时间
这些API位于:kernel\timer.c中,该源文件里还包括了常用的msleep(),schedule_timeout()等常用的延时调度函数。
下面以一个实例驱动介绍linux里的timer的使用(基于3.10.0-123内核)。
案例如下:
- 创建一定时器,完成每3s一次周期性中断并打印。
- 通过字符设备接口与用户层交互,写0则关闭(停止)定时器,写1则开启(启动)定时器
(即:当echo 1 >/dev/demo_deva启动定时器,当echo 0 >/dev/demo_deva 停止定时器)。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include "timer_drv.h"
/*
功能:
定时器3秒定时中断一次打印。
当echo 1 >/dev/demo_deva开启定时器,当echo 0 >/dev/demo_deva 关闭定时器
*/
#define DRV_VERSION "V1.0"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LUDY");
MODULE_DESCRIPTION("This is timer demo");
MODULE_VERSION(DRV_VERSION);
//static volatile int time_count = 0;
static void handel_irq_do_timer(unsigned long arg);
u8 delay = 3; //3s
/* 设备要使用的定时器 */
static struct timer_list my_timer;//也可以:TIMER_INITIALIZER(handel_irq_do_timer, 0, 0); 直接快速初始化结构体内容
/* 定时器中断处理函数 */
static void handel_irq_do_timer(unsigned long arg)
{
// struct xxx_device *dev = (struct xxx_device *)(arg);
/* 修改调度定时器,3s再执行 */
mod_timer(&my_timer, jiffies + delay*HZ); //*HZ即转换成jiffies单位值
printk("timer arrival\n");
}
/* 定时器初始化*/
void timer_init_run(void* pri_data)
{
//device *dev = (device*)pri_data;
/* 初始化定时器 */
init_timer(&my_timer);
/* 设备结构体指针作为定时器处理函数参数 */
my_timer.function = &handel_irq_do_timer; //中断处理函数
my_timer.expires = delay*HZ; //定时时间delay秒
//my_timer.data = (unsigned long)dev;
}
/*
写函数;
定时器启动和停止控制
*/
ssize_t demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
char rev_data;
/*只接受一个字符的写入*/
if(count >2) //count包括了/0字符
return -EFAULT;
//copy_from_user(rev_data1, buf, count);
if(get_user(rev_data, buf))
{
return -EFAULT;
}
printk("driver: device write:%c count:%d\n" ,rev_data, count);
switch(rev_data)
{
case '0':
/* 删除(停止)定时器 */
del_timer(&my_timer);
break;
case '1':
/* 添加(注册) 启动定时器 */
add_timer(&my_timer); //add添加后,定制器开始运行
break;
default:
PRINT_ERR("write EINVAL:%c\n" ,rev_data);
break;
}
return count;
}
ssize_t demodrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
return 1;
}
static int demodrv_open(struct inode *inode, struct file *file)
{
printk("driver: device open\n");
return 0;
}
int demodrv_close(struct inode *inode, struct file *file)
{
printk("driver: device close\n");
return 0;
}
static struct file_operations demo_drv_fops = {
.owner = THIS_MODULE,
.open = demodrv_open,
.read = demodrv_read,
.write = demodrv_write,
.release = demodrv_close,
};
int major;
static struct class *demo_drv_class;
static struct class_device *demo_class_dev;
static int __init demo_drv_init(void)
{
major = register_chrdev(0, "demo_drv", &demo_drv_fops);//注册字符驱动获取设备号
demo_drv_class = class_create(THIS_MODULE, "demo_drv_class"); //sys/class下创建类
demo_class_dev = device_create(demo_drv_class, NULL, MKDEV(major, 0), NULL, "demo_dev"); /*类下创建设备文件绑定到设备号 /dev/demo_dev */
timer_init_run(demo_class_dev);
return 0;
}
static void __exit demo_drv_exit(void)
{
unregister_chrdev(major, "demodrv");
device_unregister(demo_class_dev);
class_destroy(demo_drv_class);
/* 删除(停止)定时器 */
del_timer(&my_timer);
return 0;
}
module_init(demo_drv_init);
module_exit(demo_drv_exit);
如上源码可知,使用一个定时器通常需要如下步骤:
- 定义一个timer_list 结构体的定时器对象。并可以通过TIMER_INITIALIZER快速赋值对象里的成员(中断函数;定时时间等);
- init_timer 初始化该定制器(此时定时器还不能走);
- 可以自定义为定时器对象里的成员赋值(中断函数;定时时间;私有数据等);
- add_timer 启动该定时器,定时器开始倒计时;
- mod_timer 修改当前定时器的数值(复位定时器);
- 如果要停止(删除)一个定时器,执行del_timer,需要重新启动则继续执行add_timer。