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