标准的定时器不够精确,不适合对时间精度要求比较高的场景,比如说实时应用。要使用高精度定时器,需要开器配置选项CONFIG_HIGH_RES_TIMES,定时的精度为微秒,与标准定时器的区别是标准定时器取决于HZ(依赖jiffies),而HRT实现是基于ktime。
基本数据结构
/*** struct hrtimer - the basic hrtimer structure* @node: timerqueue node, which also manages node.expires,* the absolute expiry time in the hrtimers internal* representation. The time is related to the clock on* which the timer is based. Is setup by adding* slack to the _softexpires value. For non range timers* identical to _softexpires.* @_softexpires: the absolute earliest expiry time of the hrtimer.* The time which was given as expiry time when the timer* was armed.* @function: timer expiry callback function* @base: pointer to the timer base (per cpu and per clock)* @state: state information (See bit values above)* @is_rel: Set if the timer was armed relative* @is_soft: Set if hrtimer will be expired in soft interrupt context.** The hrtimer structure must be initialized by hrtimer_init()*/
struct hrtimer {struct timerqueue_node node;ktime_t _softexpires;enum hrtimer_restart (*function)(struct hrtimer *);struct hrtimer_clock_base *base;u8 state;u8 is_rel;u8 is_soft;
};
使用步骤
1)初始化hrtimer之前,需要设置ktime,它代表持续时间。
/*** hrtimer_init - initialize a timer to the given clock* @timer: the timer to be initialized* @clock_id: the clock to be used* @mode: The modes which are relevant for intitialization:* HRTIMER_MODE_ABS, HRTIMER_MODE_REL, HRTIMER_MODE_ABS_SOFT,* HRTIMER_MODE_REL_SOFT** The PINNED variants of the above can be handed in,* but the PINNED bit is ignored as pinning happens* when the hrtimer is started*/
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,enum hrtimer_mode mode)
{debug_init(timer, clock_id, mode);__hrtimer_init(timer, clock_id, mode);
}
EXPORT_SYMBOL_GPL(hrtimer_init);
2)启动hrtimer
/*** hrtimer_start - (re)start an hrtimer* @timer: the timer to be added* @tim: expiry time* @mode: timer mode: absolute (HRTIMER_MODE_ABS) or* relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);* softirq based mode is considered for debug purpose only!*/
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,const enum hrtimer_mode mode)
{hrtimer_start_range_ns(timer, tim, 0, mode);
}
HRTIMER_MODE_ABS
,相对时间HRTIMER_MODE_REL
/** Mode arguments of xxx_hrtimer functions:** HRTIMER_MODE_ABS - Time value is absolute* HRTIMER_MODE_REL - Time value is relative to now* HRTIMER_MODE_PINNED - Timer is bound to CPU (is only considered* when starting the timer)* HRTIMER_MODE_SOFT - Timer callback function will be executed in* soft irq context*/
enum hrtimer_mode {HRTIMER_MODE_ABS = 0x00,HRTIMER_MODE_REL = 0x01,HRTIMER_MODE_PINNED = 0x02,HRTIMER_MODE_SOFT = 0x04,HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,HRTIMER_MODE_ABS_SOFT = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,HRTIMER_MODE_REL_SOFT = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,HRTIMER_MODE_ABS_PINNED_SOFT = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_SOFT,HRTIMER_MODE_REL_PINNED_SOFT = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_SOFT,};
3)取消hrtimer,取消定时器或者查看是否可以取消。
/*** hrtimer_try_to_cancel - try to deactivate a timer* @timer: hrtimer to stop** Returns:* 0 when the timer was not active* 1 when the timer was active* -1 when the timer is currently executing the callback function and* cannot be stopped*/
int hrtimer_try_to_cancel(struct hrtimer *timer)
{struct hrtimer_clock_base *base;unsigned long flags;int ret = -1;/** Check lockless first. If the timer is not active (neither* enqueued nor running the callback, nothing to do here. The* base lock does not serialize against a concurrent enqueue,* so we can avoid taking it.*/if (!hrtimer_active(timer))return 0;base = lock_hrtimer_base(timer, &flags);if (!hrtimer_callback_running(timer))ret = remove_hrtimer(timer, base, false);unlock_hrtimer_base(timer, &flags);return ret;}
EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel);/*** hrtimer_cancel - cancel a timer and wait for the handler to finish.* @timer: the timer to be cancelled** Returns:* 0 when the timer was not active* 1 when the timer was active*/
int hrtimer_cancel(struct hrtimer *timer)
{for (;;) {int ret = hrtimer_try_to_cancel(timer);if (ret >= 0)return ret;cpu_relax();}
}
EXPORT_SYMBOL_GPL(hrtimer_cancel);
定时器没有被激活时都返回0,激活时返回1。
如果定时器处于激活状态或者回调函数正在运行,hrtimer_cancel将会等待回调完成,hrtimer_try_to_cancel会失败并返回-1。
如何判断hrtimer回调函数是否正在运行?
/** Helper function to check, whether the timer is running the callback* function*/
static inline int hrtimer_callback_running(struct hrtimer *timer)
{return timer->base->running == timer;
}
为了防止定时器自动重启,hrtimer回调函数必须返回HRTIMER_NORESTART
/** Return values for the callback function*/
enum hrtimer_restart {HRTIMER_NORESTART, /* Timer is not restarted */HRTIMER_RESTART, /* Timer must be restarted */
};
检查系统是否支持HRT
root@curtis-Aspire-E5-471G:/home/curtis/# cat /proc/timer_list | grep resolution.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecs.resolution: 1 nsecsroot@curtis-Aspire-E5-471G:/home/curtis/# cat /proc/timer_list
Tick Device: mode: 1
Per CPU device: 3
Clock Event Device: lapicmax_delta_ns: 344382579857min_delta_ns: 2406mult: 26782342shift: 32mode: 3next_event: 3069804027141 nsecsset_next_event: lapic_next_eventshutdown: lapic_timer_shutdownperiodic: lapic_timer_set_periodiconeshot: lapic_timer_set_oneshotoneshot stopped: lapic_timer_shutdownevent_handler: hrtimer_interruptretries: 212
Wakeup Device:
root@curtis-Aspire-E5-471G:/home/curtis/write_code/kmodule/kprobe_example# cat /usr/src/linux-headers-5.15.0-52-generic/.config | grep CONFIG_HIGH_RES_TIMERS
CONFIG_HIGH_RES_TIMERS=y
使用实例
hrtimer.c
#include
#include
#include
#include
#include
#include
#include
#include
#include //Timer Variable
#define TIMEOUT_NSEC ( 1000000000L ) //1 second in nano seconds
#define TIMEOUT_SEC ( 4 ) //4 secondsstatic struct hrtimer etx_hr_timer;
static unsigned int count = 0;dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);/*************** Driver functions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
/******************************************************///File operation structure
static struct file_operations fops =
{.owner = THIS_MODULE,.read = etx_read,.write = etx_write,.open = etx_open,.release = etx_release,
};//Timer Callback function. This will be called when timer expires
enum hrtimer_restart timer_callback(struct hrtimer *timer)
{/* do your timer stuff here */pr_info("Timer Callback function Called [%d]\n",count++);hrtimer_forward_now(timer,ktime_set(TIMEOUT_SEC, TIMEOUT_NSEC));return HRTIMER_RESTART;
}/*
** This function will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{pr_info("Device File Opened...!!!\n");return 0;
}/*
** This function will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{pr_info("Device File Closed...!!!\n");return 0;
}/*
** This function will be called when we read the Device file
*/
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{pr_info("Read Function\n");return 0;
}/*
** This function will be called when we write the Device file
*/
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{pr_info("Write function\n");return len;
}/*
** Module Init function
*/
static int __init etx_driver_init(void)
{ktime_t ktime;/*Allocating Major number*/if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){pr_err("Cannot allocate major number\n");return -1;}pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));/*Creating cdev structure*/cdev_init(&etx_cdev,&fops);/*Adding character device to the system*/if((cdev_add(&etx_cdev,dev,1)) < 0){pr_err("Cannot add the device to the system\n");goto r_class;}/*Creating struct class*/if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){pr_err("Cannot create the struct class\n");goto r_class;}/*Creating device*/if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){pr_err("Cannot create the Device 1\n");goto r_device;}ktime = ktime_set(TIMEOUT_SEC, TIMEOUT_NSEC);hrtimer_init(&etx_hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);etx_hr_timer.function = &timer_callback;hrtimer_start( &etx_hr_timer, ktime, HRTIMER_MODE_REL);pr_info("Device Driver Insert...Done!!!\n");return 0;
r_device:class_destroy(dev_class);
r_class:unregister_chrdev_region(dev,1);return -1;
}/*
** Module exit function
*/
static void __exit etx_driver_exit(void)
{//stop the timerhrtimer_cancel(&etx_hr_timer);device_destroy(dev_class,dev); class_destroy(dev_class);cdev_del(&etx_cdev);unregister_chrdev_region(dev, 1);pr_info("Device Driver Remove...Done!!!\n");
}module_init(etx_driver_init);
module_exit(etx_driver_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple device driver - High Resolution Timer");
MODULE_VERSION("1.22");
obj-m += hrtimer.oKDIR = /lib/modules/$(shell uname -r)/buildall:make -C $(KDIR) M=$(shell pwd) modulesclean:make -C $(KDIR) M=$(shell pwd) clean
下一篇:VI的常用命令