1 hrtimer 概述
在Linux内核中已经存在了一个管理定时器的通用框架。不过它也有很多不足,最大的问题是其精度不是很高。哪怕底层的定时事件设备精度再高,定时器层的分辨率只能达到Tick级别,按照内核配置选项的不同,在100Hz到1000Hz之间。但是,原有的定时器层由于实现教早,应用广泛,如果完全替换掉会引入大量代码改动。因此,Linux内核又独立设计出了一个叫高精度定时器层(High Resolution Timer)的框架,可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动程序。
高分辨率定时器是建立在每CPU私有独占的本地时钟事件设备上的,对于一个多处理器系统,如果只有全局的时钟事件设备,高分辨率定时器是无法工作的。因为如果没有每CPU私有独占的时钟事件设备,当到期中断发生时系统必须产生夸处理器中断来通知其它CPU完成相应的工作,而过多的夸处理器中断会带来很大的系统开销,这样会令使用高分辨率定时器的代价大大增加,还不如不用。为了让内核支持高分辨率定时器,必须要在编译的时候打开编译选项CONFIG_HIGH_RES_TIMERS。
高分辨率定时器层有两种工作模式:低精度模式与高精度模式。虽然高分辨率定时器子系统是为高精度定时器准备的,但是系统可能在运行过程中动态切换到不同精度和模式的定时事件设备,因此高精度定时器层必须能够在低精度模式与高精度模式下自由切换。
高分辨率定时器层使用红黑树来组织各个高分辨率定时器。随着系统的运行,高分辨率定时器不停地被创建和销毁,新的高分辨率定时器按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器。
2 相关数据结构
2.1 hrtimer
高分辨率定时器由hrtimer结构体表示(代码位于include/linux/hrtimer.h中):
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
u8 state;
u8 is_rel;
u8 is_soft;
u8 is_hard;
};
node:是一个timerqueue_node结构体变量。这个结构体中有两个成员,node是红黑树的节点,expires:表示该定时器的硬超时时间:
struct timerqueue_node {
struct rb_node node;
ktime_t expires;
};
_softexpires:表示该定时器的软超时时间。高精度定时器一般都有一个到期的时间范围,而不像(低精度)定时器那样就是一个时间点。这个时间范围的前时间点就是软超时时间,而后一个时间点就是硬超时时间。达到软超时时间后,还可以再拖一会再调用超时回调函数,而到达硬超时时间后就不能再拖了。
function:定时器到期后的回调函数。
base:指向包含该高分辨率定时器的的hrtimer_clock_base结构体。
state:用来表示该高分辨率定时器当前所处的状态,目前共有两种状态:
/* 表示定时器还未激活 */
#define HRTIMER_STATE_INACTIVE 0x00
/* 表示定时器已激活(入列) */
#define HRTIMER_STATE_ENQUEUED 0x01
is_rel:表示该定时器的到期时间是否是相对时间。is_soft:表示该定时器是否是“软”定时器。
is_hard:表示该定时器是否是“硬”定时器。
2.2 hrtimer_clock_base
struct hrtimer_clock_base {
struct hrtimer_cpu_base *cpu_base;
unsigned int index;
clockid_t clockid;
seqcount_t seq;
struct hrtimer *running;
struct timerqueue_head active;
ktime_t (*get_time)(void);
ktime_t offset;
} __hrtimer_clock_base_align;
cpu_base:指向所属CPU的hrtimer_cpu_base结构体。
index:表示该结构体在当前CPU的hrtimer_cpu_base结构体中clock_base数组中所处的下标。
clockid:表示当前时钟类型的ID值。
seq:顺序锁,在处理到期定时器的函数__run_hrtimer中会用到。
running:指向当前正在处理的那个定时器。
active:红黑树,包含了所有使用该时间类型的定时器。
get_time:是一个函数指针,指定了如何获取该时间类型的当前时间的函数。由于不同类型的时间在Linux中都是由时间维护层来统一管理的,因此这些函数都是在时间维护层里面定义好的。
offset:表示当前时间类型和单调时间之间的差值。
2.3 hrtimer_cpu_base
每个CPU单独管理属于自己的高分辨率定时器,为了方便管理,专门定义了一个结构体hrtimer_cpu_base:
struct hrtimer_cpu_base {
raw_spinlock_t lock;
unsigned int cpu;
unsigned int active_bases;
unsigned int clock_was_set_seq;
unsigned int hres_active : 1,
in_hrtirq : 1,
hang_detected : 1,
softirq_activated : 1;
#ifdef CONFIG_HIGH_RES_TIMERS
unsigned int nr_events;
unsigned short nr_retries;
unsigned short nr_hangs;
unsigned int max_hang_tim