概述
前言
本次实验,在hrtimer定时器中输出5个计数值。
头文件:
#include <hrtimer.h>
struct hrtimer 机构体:
/**
* 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)
* @start_pid: timer statistics field to store the pid of the task which
* started the timer
* @start_site: timer statistics field to store the site where the timer
* was started
* @start_comm: timer statistics field to store the name of the process which
* started the timer
*
* 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;
unsigned long state;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
};
enum hrtimer_restart
/*
* Return values for the callback function
*/
enum hrtimer_restart {
HRTIMER_NORESTART, /* Timer is not restarted */
HRTIMER_RESTART, /* Timer must be restarted */
};
一 高精度定时器
linux内核支持tickless和NO_HZ模式后,内核也包含对hrtimer(高精度定时器)的支持,它可以支持到微秒级别的精度。内核也定义了hrtimer结构体,hrtimer_set_expires()、hrtimer_start_expires()、hrtimer_forward_now()、hrtimer_restart()等类似的API来完成hrtimer的设置、时间推移以及到期回调。我们可以从sound/soc/fsl/imx-pcm-fiq.c中提取出一个使用范例,如下所示。
1 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
2 {
3 ...
4
5 hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns));
6
7 return HRTIMER_RESTART;
8 }
9
10 static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
11 {
12 struct snd_pcm_runtime *runtime = substream->runtime;
13 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
14
15 switch (cmd) {
16 case SNDRV_PCM_TRIGGER_START:
17 case SNDRV_PCM_TRIGGER_RESUME:
18 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
19 ...
20 hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns),
21 HRTIMER_MODE_REL);
22 ...
23 }
24
25 static int snd_imx_open(struct snd_pcm_substream *substream)
26{
27 ...
28 hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
29 iprtd->hrt.function = snd_hrtimer_callback;
30
31 ...
32 return 0;
33 }
34 static int snd_imx_close(struct snd_pcm_substream *substream)
35 {
36 ...
37 hrtimer_cancel(&iprtd->hrt);
38 ...
39 }
第28~29行在声卡打开的时候通过hrtimer_init()初始化了hrtimer,并指定回调函数为snd_hrtimer_callback();在启动播放(第15~21行SNDRV_PCM_TRIGGER_START)等时刻通过hrtimer_start()启动了hrtimer;iprtd->poll_time_ns纳秒后,时间到snd_hrtimer_callback()函数在中断上下文被执行,它紧接着又通过hrtimer_forward_now()把hrtimer的时间前移了iprtd->poll_time_ns纳秒,这样周而复始;直到声卡被关闭,第37行又调用了hrtimer_cancel()取消在open时初始化的hrtimer。
二 函数分析
从这些函数名字能看出来他们是干什么的吗?能吧???,例如:add_timer和mod_timer,是不是开始怀疑了,怀疑就对了。
hrtimer_init
clock_id参数取值:CLOCK_REALTIME和CLOCK_MONOTONIC;
CLOCK_MONOTONIC:以绝对时间为准,获取的时间为系统重启到现在的时间,更改系统时间对它没有影响。字面意义:单调时间,表示系统启动后流逝的时间,由变量jiffies来记录的。系统每次启动时,jiffies初始化为0。每来一个timer interrupt,jiffies加1,即它代表系统启动后流逝的tick数。jiffies一定是单调递增的,因为时间不可逆。
CLOCK_REALTIME(即wall time)
CLOCK_REALTIME:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值。它以系统时间为坐标。字面意思: wall time挂钟时间,表示现实的时间,由变量xtime来记录的。系统每次启动时,将CMOS上的RTC时间读入xtime,这个值是”自1970-01-01起经历的秒数、本秒中经历的纳秒数”。每来一个timer interrupt,也需要去更新xtime。wall time不一定是单调递增的。因为wall time是指现实中的实际时间,如果系统要与网络中某个节点时间同步、或者由系统管理员觉得这个wall time与现实时间不一致,有可能任意的改变这个wall time。最简单的例子是,用户本身可以去任意修改系统时间,这个被修改的时间应该就是wall time,即xtime,它甚至可以被写入RTC而永久保存。
mode参数取值:HRTIMER_MODE_ABS或者HRTIMER_MODE_REL;
/*
* Mode arguments of xxx_hrtimer functions:
*/
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 */
HRTIMER_MODE_ABS_PINNED = 0x02,
HRTIMER_MODE_REL_PINNED = 0x03,
};
typedef int __kernel_clockid_t;
typedef __kernel_clockid_t clockid_t;
/**
* hrtimer_init - initialize a timer to the given clock
* @timer: the timer to be initialized
* @clock_id: the clock to be used
* @mode: timer mode abs/rel
*/
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);
}
hrtimer_start
him参数可以使相对时间也可以使绝对时间,本次实验,我们使用相对时间。它可以通过ktime_set函数构造
/**
* hrtimer_start - (re)start an hrtimer on the current CPU
* @timer: the timer to be added
* @tim: expiry time
* @mode: expiry mode: absolute (HRTIMER_MODE_ABS) or
* relative (HRTIMER_MODE_REL)
*
* Returns:
* 0 on success
* 1 when the timer was active
*/
int
hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
{
return __hrtimer_start_range_ns(timer, tim, 0, mode, 1);
}
ktime_set函数
从定义可知ktime_t 是一个s64类型。第一个参数表示秒,第二个参数表示纳秒。1000纳秒=1毫秒1000微秒=1毫秒,1毫秒等于百万纳秒。这精度感觉和我没啥关系呢。
/*
* ktime_t:
*
* A single 64-bit variable is used to store the hrtimers
* internal representation of time values in scalar nanoseconds. The
* design plays out best on 64-bit CPUs, where most conversions are
* NOPs and most arithmetic ktime_t operations are plain arithmetic
* operations.
*
*/
union ktime {
s64 tv64;
};
typedef union ktime ktime_t; /* Kill this */
/**
* ktime_set - Set a ktime_t variable from a seconds/nanoseconds value
* @secs: seconds to set
* @nsecs: nanoseconds to set
*
* Return: The ktime_t representation of the value.
*/
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_to_ms
static inline s64 ktime_to_ms(const ktime_t kt)
{
return ktime_divns(kt, NSEC_PER_MSEC);
}
ms_to_ktime:本次实验用到的
static inline ktime_t ms_to_ktime(u64 ms)
{
static const ktime_t ktime_zero = { .tv64 = 0 };
return ktime_add_ms(ktime_zero, ms);
}
hrtimer_set_expires
static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
{
timer->node.expires = time;
timer->_softexpires = time;
}
hrtimer_start_expires
static inline int hrtimer_start_expires(struct hrtimer *timer,
enum hrtimer_mode mode)
{
unsigned long delta;
ktime_t soft, hard;
soft = hrtimer_get_softexpires(timer);
hard = hrtimer_get_expires(timer);
delta = ktime_to_ns(ktime_sub(hard, soft));
return hrtimer_start_range_ns(timer, soft, delta, mode);
}
hrtimer_forward_now
/* Forward a hrtimer so it expires after the hrtimer's current now */
static inline u64 hrtimer_forward_now(struct hrtimer *timer,
ktime_t interval)
{
return hrtimer_forward(timer, timer->base->get_time(), interval);
}
hrtimer_forward
/**
* hrtimer_forward - forward the timer expiry
* @timer: hrtimer to forward
* @now: forward past this time
* @interval: the interval to forward
*
* Forward the timer expiry so it will expire in the future.
* Returns the number of overruns.
*/
u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
{
u64 orun = 1;
ktime_t delta;
delta = ktime_sub(now, hrtimer_get_expires(timer));
if (delta.tv64 < 0)
return 0;
if (interval.tv64 < timer->base->resolution.tv64)
interval.tv64 = timer->base->resolution.tv64;
if (unlikely(delta.tv64 >= interval.tv64)) {
s64 incr = ktime_to_ns(interval);
orun = ktime_divns(delta, incr);
hrtimer_add_expires_ns(timer, incr * orun);
if (hrtimer_get_expires_tv64(timer) > now.tv64)
return orun;
/*
* This (and the ktime_add() below) is the
* correction for exact:
*/
orun++;
}
hrtimer_add_expires(timer, interval);
return orun;
}
hrtimer_restart
static inline int hrtimer_restart(struct hrtimer *timer)
{
return hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
}
hrtimer_start_expires
static inline int hrtimer_start_expires(struct hrtimer *timer,
enum hrtimer_mode mode)
{
unsigned long delta;
ktime_t soft, hard;
soft = hrtimer_get_softexpires(timer);
hard = hrtimer_get_expires(timer);
delta = ktime_to_ns(ktime_sub(hard, soft));
return hrtimer_start_range_ns(timer, soft, delta, mode);
}
三 测试例程
源码:csi_timer.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/hrtimer.h>
#define DEBUG_CT(format,...)
printk("%s:%s:%d: "format"n",
__FILE__,__func__,__LINE__,
##__VA_ARGS__)
struct ct_dev_{
struct hrtimer hrtimer;
int count;
};
struct ct_dev_ *ct_dev;
static enum hrtimer_restart ct_timer_function(struct hrtimer *timer)
{
struct ct_dev_ *p =
(struct ct_dev_ *)container_of(timer, struct ct_dev_, hrtimer);
DEBUG_CT("p->count = %d",p->count++);
if(p->count < 5){
#if 1
return HRTIMER_RESTART;
#else
hrtimer_start(&p->hrtimer, ms_to_ktime(1000), HRTIMER_MODE_REL);
#endif
}
return HRTIMER_NORESTART;
}
static int __init ct_init(void)
{
struct ct_dev_ *p = NULL;
ct_dev = (struct ct_dev_ *)kmalloc(sizeof(struct ct_dev_),GFP_KERNEL);
if(IS_ERR(ct_dev)){
DEBUG_CT("kmalloc error");
return -ENOMEM;
}
p = ct_dev;
hrtimer_init(&p->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
p->hrtimer.function = ct_timer_function;
//hrtimer_start(&p->hrtimer, ktime_set(1,0), HRTIMER_MODE_REL);
hrtimer_start(&p->hrtimer, ms_to_ktime(1000), HRTIMER_MODE_REL);
p->count = 0;
DEBUG_CT("init ok");
return 0;
}
static void __exit ct_exit(void)
{
struct ct_dev_ *p = ct_dev;
if(IS_ERR(p)){
return;
}
kfree(p);
DEBUG_CT("exit ok");
}
module_init(ct_init);
module_exit(ct_exit);
MODULE_LICENSE("GPL");
makefile文件:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERNELDIR := /home/lkmao/imx/linux/linux-imx
CURRENT_PATH := $(shell pwd)
FILE_NAME=csi_timer
obj-m := $(FILE_NAME).o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
测试结果:
测试1:
发现定时器中的调试信息连续被输出的,中间没有停顿。这都是HRTIMER_RESTART的作用。
root@hehe:~# insmod csi_timer.ko
[10025.212784] /big/csi_driver/csi_timer/csi_timer.c:ct_init:48: init ok
root@hehe:~# [10026.212804] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 0
[10026.220575] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 1
[10026.228329] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 2
[10026.236079] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 3
[10026.243829] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 4
测试2:
修改ct_timer_function函数,如下所示:
static enum hrtimer_restart ct_timer_function(struct hrtimer *timer)
{
struct ct_dev_ *p =
(struct ct_dev_ *)container_of(timer, struct ct_dev_, hrtimer);
DEBUG_CT("p->count = %d",p->count++);
if(p->count < 5){
#if 0
return HRTIMER_RESTART;
#else
hrtimer_start(&p->hrtimer, ms_to_ktime(1000), HRTIMER_MODE_REL);
#endif
}
return HRTIMER_NORESTART;
}
测试结果如下:从条信息前面的中括号中的时间可知,现在的调试信息间隔明显拉开了。并且也是输出5次。
root@hehe:~# insmod csi_timer.ko
[10262.614133] /big/csi_driver/csi_timer/csi_timer.c:ct_init:48: init ok
root@hehe:~# [10263.614151] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 0
[10264.621943] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 1
[10265.629736] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 2
[10266.637534] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 3
[10267.645320] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:22: p->count = 4
结束
最后
以上就是认真玉米为你收集整理的linux内核高精度定时器:hrtimer前言一 高精度定时器二 函数分析三 测试例程结束的全部内容,希望文章能够帮你解决linux内核高精度定时器:hrtimer前言一 高精度定时器二 函数分析三 测试例程结束所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复