内核中的高精度定时器
创始人
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

相关内容

热门资讯

汽车产业已经有“恒大”了?价格... 中国汽车产业即将迎来重大考验!近日,以直率著称的长城汽车董事长魏建军在接受媒体专访时,发出惊人警告:...
九牧的三重跃升:新场景、新智造... AI时代,中国卫浴加速征战世界。文 | 华商韬略 熊剑辉当上过春晚的宇树人形机器人,化身“礼仪小姐姐...
限流|限流算法 一、概念限流顾名思义,就是对请求或并发数进行限制;通过对一个时间窗口内的...
浙江金融反腐风暴:工行浙江分行... 靴子落地。5月30日,据中央纪委国家监委驻中国工商银行纪检监察组、辽宁省纪委监委消息:中国工商银行浙...
四年前收购的一家公司,如今要上... 这位实控人四年前收购了个企业,现在就准备让公司在港交所上市了!5月29日,港交所官网披露,江苏日御光...
美的集团董事长方洪波:小米进入... 5月30日,美的集团(000333.SZ、00300.HK,简称美的)董事长兼总裁方洪波在美的集团2...
产品研发利好引发连日大涨 舒泰... 新京报贝壳财经讯(记者丁爽)5月30日,舒泰神再度大涨收场。至当日收盘,公司股价涨15.02%,报2...
sql 标准的隔离级别 事务并发执行时遇到的一致性问题脏读(Dirty Read)如果一个事务读...
【读书笔记】电子商务 目录1,四种业务模式2,三种电商模式2.1 运营模式:“流...
5月收官,个股平均涨5%!机构... A股5月收官,全月大盘维持震荡,概念题材热点飞速轮动。今年的5月并不是“5穷”,两市5000余只个股...
说到底,钱生钱就这4个办法|小... 点击 “简七读财” ,发送消息“理财小工具”免费领取40个赚钱工具资源包~晚上好呀~不少朋友吐槽,...
一杯咖啡3块钱,“外卖补贴”带... 本文来源:时代周报 作者:孙艺格 图源:库迪咖啡官网1元的超大杯奶茶、1.68元的美式咖啡、2.6...
中国联塑创始人之子上任执行董事... 一则人事变动,让国内管道建材头部企业中国联塑集团控股有限公司(以下简称“中国联塑”)受到关注。中国联...
谷粒学院SpringSecur... 登录功能前端分析前端会调用此接口去实现登录// 登录export function login(us...
【基于协同过滤算法的推荐系统项... 本文目录1、推荐系统的关键元素1.1 数据1.2 算法1.3 业务领域1.4 展示信息2、推荐算法的...
公告精选丨中国交建:拟以5亿元... 今日焦点中国交建:拟以5亿元-10亿元回购公司A股股份中国交建公告称,公司拟不低于5亿元,不超过10...
javascript数组常用方... 数组对于程序语言的重要性自不必多说,而在使用javascript的时候难免也会需要用到...
SpringBoot学习--基... 本专栏主要记录SpringBoot学习之路 文章目录1 SpringBoot基本介绍1.1 官方文档...
重组停牌!渤海汽车拟购海纳川旗... 5月30日,渤海汽车(600960.SH)发布公告称,公司拟通过发行股份及支付现金的方式,购买北京海...
Spring学习(五) 事物管理: 一、事物管理的回顾: 1、事物的概念: 事物&...
java线程同步 并发:同一个对象被多个线程同时操作处理多线程问题时,多个线程访问同一个对...
代码审计(二) 一、DevSecOps的概念DevSecOps 是描述开发、安全和运营集成的术语。它是一种文化、自动...
西安高新“楼市新政”,一场“教... 第 2302期〡2025/05/30西安各区域的土地市场和区域价值或将重新洗牌。上周,西安高新区一份...
603023,下周复牌!正式摘... 2025.05.30本文字数:855,阅读时长大约2分钟5月30日,*ST威帝公告称,公司股票将于2...
第十章 STM32+ESP82... 前言 最近有不少小伙伴私信留言,想要我推出一章能够通过APP进行远程控制并获取传感器信...
试题 算法训练 逗志芃的暴走 问题描述   逗志芃是有妹子的现充,但是有时候妹子就是烦恼。因为逗志芃太逗了ÿ...
德邦证券董事会改组完成,山东财... 山东财金集团披露,5月29日,德邦证券在济南召开2025年度第一次临时股东会,会议选举产生公司新一届...
法网:郑钦文2-0胜资格赛黑马... 北京时间5月30日,2025赛季网球大满贯法国公开赛继续进行,在女单第三轮的一场比赛中,赛会8号种子...
宗馥莉接手父亲名下娃哈哈实业公... 天眼查App显示,近日,浙江娃哈哈实业股份有限公司发生工商变更,宗庆后卸任法定代表人、董事长、总经理...
北交所上市公司恒拓开源大宗交易... 每经讯,2025年5月30日,北交所上市公司恒拓开源(834415,收盘价:17.83元)发生一笔大...