Linux内核timer与hr_timer

文章详细介绍了Linux内核中的高精度定时器hr_timer和普通定时器timer的使用,包括它们的初始化、参数说明、工作模式以及相关注意事项。hr_timer支持更高精度的时钟源,如CLOCK_MONOTONIC,而timer使用jiffies进行计时。回调函数中需要注意避免休眠操作,并正确设置周期调用。此外,文章还提到了HZ、jiffies和tick等相关时间概念。
摘要由CSDN通过智能技术生成

hr_timer

hr_timer.h
hr_timer.c

使用介绍

1、引入头文件
hr_timer.h
timer.h
2、结构体中声明hr_timer
struct hrtimer hrtimer_name;
3、初始化一个内核定时器,选择时钟源,选择hrtimer模式
hrtimer_init()
4、定义定时器超时后的回调函数,函数执行后返回HRTIMER_NORESTART或HRTIMER_RESTART。如果返回HRTIMER_RESTART则定时器重新被激活。
hr_timer.function = callback_name();
5、开启定时器,同时设置超时时间,和hrtimer模式
hrtimer_start();
6、如果需要指定到期范围,使用hrtimer_start_range_ns来激活定时器
hrtimer_start_rangs_ns();
7、取消定时器
hrtimer_cancel();
8、退后定时器到期时间
hrtimer_forward_now();
9、获取定时器状态函数
hrtimer_active();
hrtimer_is_queued();
hrtimer_callback_runing();

参数说明

1、void hrtimer_init(struct hrtimer *timer,clockid_t clock_id,enum hrtimer_mode mode)
*timer:要使用的定时器
clock_id:选择要使用的时钟源
clock_id说明:

The IDs of the various system clocks (for POSIX.1b interval timers):
#define CLOCK_REALTIME			0--系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
#define CLOCK_MONOTONIC			1--从系统启动这一刻起开始计时,不受系统时间被用户改变的影响,单调递增,由变量jiffies来记录。
#define CLOCK_PROCESS_CPUTIME_ID	2—系统为每个进程提供的高精度时钟
#define CLOCK_THREAD_CPUTIME_ID		3—系统为每个线程提供的高精度时钟
#define CLOCK_MONOTONIC_RAW		4
#define CLOCK_REALTIME_COARSE		5
#define CLOCK_MONOTONIC_COARSE		6
#define CLOCK_BOOTTIME			7
#define CLOCK_REALTIME_ALARM		8
#define CLOCK_BOOTTIME_ALARM		9
#define CLOCK_SGI_CYCLE			10	/* Hardware specific */
#define CLOCK_TAI			11
#define MAX_CLOCKS			16
#define CLOCKS_MASK			(CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO			CLOCK_MONOTONIC

2、static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
*timer:要使用的定时器
tim:超时时间
mode:hrtimer的工作模式
工作模式说明:

enum hrtimer_mode {
		HRTIMER_MODE_ABS = 0x0,		/* Time value is absolute */绝对模式
		HRTIMER_MODE_REL = 0x1,		/* Time value is relative to now */相对模式
		HRTIMER_MODE_PINNED = 0x02,	/* Timer is bound to CPU */和CPU绑定
		HRTIMER_MODE_ABS_PINNED = 0x02,
		HRTIMER_MODE_REL_PINNED = 0x03,
}

3、static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
设置超时时间
函数原型:

static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
{
	if (unlikely(secs >= KTIME_SEC_MAX))
		return (ktime_t){ .tv64 = KTIME_MAX }return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs };
}

这里可以看出ktime_set返回的是两个参数时间合,单位是纳秒。
/* Parameters used to convert the timespec values: */
#define MSEC_PER_SEC 1000L—每秒有1000毫秒
#define USEC_PER_MSEC 1000L—每毫秒有1000微秒
#define NSEC_PER_USEC 1000L—每微秒有1000纳秒
#define NSEC_PER_MSEC 1000000L
#define USEC_PER_SEC 1000000L
#define NSEC_PER_SEC 1000000000L
#define FSEC_PER_SEC 1000000000000000LL—飞秒,暂不使用
使用举例:
ktime_set(0, 25 * NSEC_PER_USEC)即25微秒。

注意事项

1、在超时回调函数中,调用可能引起休眠的操作会导致内核Bug,回调函数的本质是中断操作。
2、如何实现hrtimer的周期调用:
(1)方法1 超时函数,调用hrtimer_start(, tim,HRTIMER_MODE_REL);即把timer根据此tim超时时间插入到timer_base的队列中, 并返回HRTIMER_NORESTART
(2)方法2 超时函数,调用hrtimer_forward()或者hrtimer_forward_now(), 把hrtimer的_softexpires和timerqueue_node.expires往后退一个interval的时间,然后函数返回HRTIMER_RESTART。 此后由__run_hrtimer()调用enqueue_hrtimer()来自动重新插入到timer_base的队列中。如果方法2中,如果不调用hrtimer_forward()或者hrtimer_forward_now(), 而直接返回HRTIMER_RESTART,那么定时函数的超时周期就变成timer_base的resolution分辨率的周期来运行了,这样的resolution,频率太高。
3、如果使用绝对时间或相对时间,hrtimer_forward_now进行周期调用,回调函数中有对时间其他的处理,比如增加延时等,则函数执行的周期为定时器的周期减去延时的时间。两次start的时间间隔为1秒
在这里插入图片描述在这里插入图片描述
4、如果使用相对时间,使用hrtimer_start进行周期调用,回调函数内的延时时间,不算入定时器延时中。hrtimer_star进行周期调用,不能使用绝对时间,会一直触发定时器中断导致狗超时。
在这里插入图片描述在这里插入图片描述

timer

timer.h
timer.c

使用介绍

1、定义一个timer
(1)使用DEFINE_TIMER宏
#define DEFINE_TIMER(_name, _function, _expires, _data)
(2)结构体声明
struct timer_list timer;
2、激活一个定时器
add_timer(&timer);
3、修改定时器到期时间
mod_timer(&timer,jiffies+50);
4、移除一个定时器
del_timer(&timer);
5、设置到期回调函数
setup_timer(timer,fn,data);
6、其他API:
void add_timer_on(struct timer_list *timer, int cpu); // 在指定的cpu上添加定时器
int mod_timer_pending(struct timer_list *timer, unsigned long expires); // 只有当timer已经处在激活状态时,才修改timer的到期时刻
void set_timer_slack(struct timer_list *time, int slack_hz); // 设定timer允许的到期时刻的最大延迟,用于对精度不敏感的定时器
int del_timer_sync(struct timer_list *timer); // 如果该timer正在被处理中,则等待timer处理完成才移除该timer

参数说明

1、int mod_timer(struct timer_list *timer,unsigned long expires)
*timer用到的定时器
expires定时器到期时间
2、timer_list结构体说明:

struct timer_list{
		struct hlist_node entry;
		unsigned long expires;
		void (*function)(unsigned long);
		unsigned long data;
		u32 flags;
}

entry 字段用于把一组定时器组成一个链表,至于内核如何对定时器进行分组。
expires 字段指出了该定时器的到期时刻,也就是期望定时器到期时刻的jiffies计数值。
function 字段是一个函数指针,定时器到期时,系统将会调用该回调函数,用于响应该定时器的到期事件
3、setup_timer(timer,fn,data);
timer所用定时器
fn定时器到期执行的函数
data回调函数所用的形参

注意事项

1)如果定时器需要循环使用,需要再处理函数的最后将定时器加入到列表中,即执行mod_timer函数即可。
2)查看时间,发现与hrtimer是有差异的,hrtimer中的周期调用,是在系统时间的基础上,往后拖延时间,timer中的周期调用是根据当前jiffies往后拖延时间,两者在到期时间上是有区别的。timer的回调函数中添加的时间处理不包括在延时中。
在这里插入图片描述
在这里插入图片描述
3) 低分辨率定时器的回调函数是执行在软件中断上下文中的,这点在写定时器的回调函数时需要注意

与时间相关的概念

1)HZ: 节拍率,即系统定时器的频率,在内核中通过HZ这个宏进行定义。在进行内核编程的时候,切记不要假设HZ不会发生变化,事实上,大多数体系结构的HZ都是可调的. 增大HZ:提高时钟中断的频率,这带来的好处是,提高了时间驱动事件的解析度与精确度à内核定时器具有更高的频度与精确度(依赖内核定时器的系统调用也有了更精确的执行度,比如select、epoll等,这会带来很大的性能提升),时间相关的测量会更准确,内核抢占更准确,进程调度的响应更及时。当然也会有负面影响:更高的中断频率,必然会导致系统消耗更多的资源来处理时钟中断.
2)jiffies: 变量类型为unsigned long volatile,该变量记录了系统启动以来,产生的tick总数,系统运行时间 = jiffies/HZ 。
3)tick:节拍,两次时钟中断之间所间隔的时间,1/HZ。

参考文章

1)Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现
https://blog.csdn.net/droidphone/article/details/8074892
2)Linux 时钟_定时器
https://blog.csdn.net/q262800095/article/details/7015741
3)Linux时间子系统之五:低分辨率定时器的原理和实现
https://blog.csdn.net/droidphone/article/details/8051405

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值