我是靠谱客的博主 认真玉米,最近开发中收集的这篇文章主要介绍linux内核高精度定时器:hrtimer前言一 高精度定时器二 函数分析三 测试例程结束,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

        本次实验,在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前言一 高精度定时器二 函数分析三 测试例程结束所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部