本次实验,在hrtimer定时器中输出5个计数值。
头文件:
#include <hrtimer.h>
struct hrtimer 机构体:
/*** struct hrtimer - the basic hrtimer structure* @node: timerqueue node, which also pires,* 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_STATSint 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 ...45 hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns));67 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,是不是开始怀疑了,怀疑就对了。
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);
}
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_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 };
}
static inline s64 ktime_to_ms(const ktime_t kt)
{return ktime_divns(kt, NSEC_PER_MSEC);
}
static inline ktime_t ms_to_ktime(u64 ms)
{static const ktime_t ktime_zero = { .tv64 = 0 };return ktime_add_ms(ktime_zero, ms);
}
static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
{timer-&pires = time;timer->_softexpires = time;
}
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);
}
/* 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 - 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;
}
static inline int hrtimer_restart(struct hrtimer *timer)
{return hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
}
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);
}
#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 1return HRTIMER_RESTART;
#elsehrtimer_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");
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).obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulessudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
发现定时器中的调试信息连续被输出的,中间没有停顿。这都是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
修改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 0return HRTIMER_RESTART;
#elsehrtimer_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
本文发布于:2024-02-05 08:01:47,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170727884564762.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |