内核中的高精度定时器
创始人
2025-05-30 02:01:09

标准的定时器不够精确,不适合对时间精度要求比较高的场景,比如说实时应用。要使用高精度定时器,需要开器配置选项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);
}
  • 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

  • 查看timer_list相关信息,.resolution为1 nsecs, event_handler: hrtimer_interrupt表示可以支持高精度定时器。
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

相关内容

热门资讯

贺博生:12.17黄金原油今日... 投资本身没有风险,失控的投资才有风险。不要用你的侥幸去挑战行情,运气这东西是有,碰上一次别再去奢望第...
HashKey港股上市:市值1... 雷递网 雷建平 12月17日 亚洲区域性数字资产在岸平台HashKey Holdings(股票代码:...
人行自贡市分行:信用“金钥匙”... “太好了!无抵押、无担保,从申请到拿到信用贷款,只用了5天,资金流信息平台真是解了我们的燃眉之急。”...
沐曦股份高开568.83%,一... 新闻荐读 17日,继摩尔线程之后,“国产GPU第二股”沐曦股份正式登陆科创板。 上市首日,沐曦股份高...
润健股份等在上海成立能源科技公... 雷达财经讯,天眼查App显示,近日,润智信科能源科技(上海)有限公司成立,法定代表人为严从东,注册资...