-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathHigh-resolution timer
More file actions
174 lines (110 loc) · 8.35 KB
/
High-resolution timer
File metadata and controls
174 lines (110 loc) · 8.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
High-resolution timer API
The hrtimer API has some similarities to the traditional API as well as some fundamental differences to account for the additional timing control. The first thing you'll notice is that time is represented not in jiffies but in a special data type called ktime. This representation hides some of the details of efficiently managing time at this granularity. The API formalizes the distinction between absolute and relative times, requiring the caller to specify the type.
Like the traditional timer API, timers are represented by a structure—in this case, hrtimer. This structure defines the timer from a user perspective (callback function, expiration time, and so on) and also incorporates the management information (where the timer exists in the red-black tree, optional statistics, and so on).
The process begins with the initialization of a timer through hrtimer_init. This call includes the timer, clock definition, and timer mode (one-shot or restart). The clock to use is defined in ./include/linux/time.h and represents the various clocks that the system supports (such as the real-time clock or a monotonic clock that simply represents time from a starting point, such as system boot). Once a timer has been initialized, it can be started with hrtimer_start. This call includes the expiration time (in ktime_t) and the mode of the time value (absolute or relative value).
void hrtimer_init( struct hrtimer *time, clockid_t which_clock, enum hrtimer_mode mode );
int hrtimer_start(struct hrtimer *timer, ktime_t time, const enum hrtimer_mode mode);
Once an hrtimer has started, it can be cancelled through a call to hrtimer_cancel or hrtimer_try_to_cancel. Each function includes the hrtimer reference as the timer to be stopped. These functions differ in that the hrtimer_cancel function attempts to cancel the timer, but if it has already fired, it will wait for the callback function to finish. The hrtimer_try_to_cancel function differs in that it also attempts to cancel the timer but will return failure if the timer has fired.
int hrtimer_cancel(struct hrtimer *timer);
int hrtimer_try_to_cancel(struct hrtimer *timer);
You can check to see if the hrtimer has activated its callback through a call to hrtimer_callback_running. Note that this function is called internally by hrtimer_try_to_cancel in order to return an error if the timer's callback function was called.
int hrtimer_callback_running(struct hrtimer *timer);
The ktime API
Not discussed here was the ktime API, which provides a rich set of functions for managing time at high resolution.
You can see the ktime API in ./linux/include/ktime.h.
An hrtimer example
Use of the hrtimer API is quite simple, as shown in Listing 2.
Within init_module, you start by defining your relative time to time-out (in this case, 200ms).
You initialize your hrtimer with a call to hrtimer_init (using the monotonic clock), and then set the callback function.
Finally, you start the timer using your previously created ktime value.
When the timer fires, the my_hrtimer_callback function is called, which returns HRTIMER_NORESTART so that the timer is not
automatically restarted. In the cleanup_module function, you clean up by cancelling the timer with hrtimer_cancel.
Listing 2. Exploring the hrtimer API
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
MODULE_LICENSE("GPL");
#define MS_TO_NS(x) (x * 1E6L)
static struct hrtimer hr_timer;
enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
{
printk( "my_hrtimer_callback called (%ld).\n", jiffies );
return HRTIMER_NORESTART;
}
int init_module( void )
{
ktime_t ktime;
unsigned long delay_in_ms = 200L;
printk("HR Timer module installing\n");
ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );
hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
hr_timer.function = &my_hrtimer_callback;
printk( "Starting timer to fire in %ldms (%ld)\n", delay_in_ms, jiffies );
hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );
return 0;
}
void cleanup_module( void )
{
int ret;
ret = hrtimer_cancel( &hr_timer );
if (ret) printk("The timer was still in use...\n");
printk("HR Timer module uninstalling\n");
return;
}
There's much more to the hrtimer API than has been touched on here.
One interesting aspect is the ability to define the execution context of the callback function (such as in softirq or hardiirq context).
You can learn more about the hrtimer API from the include file in ./include/linux/hrtimer.h
//----------------------------------------------------------------------------------------------
低分辨率定时器使用5个链表数组来组织timer_list结构,形成了著名的时间轮概念,对于高分辨率定时器,我们期望组织它们的数据结构至少具备以下条件:
1. 稳定而且快速的查找能力;
2. 快速地插入和删除定时器的能力;
3. 排序功能;
内核的开发者考察了多种数据结构,例如基数树、哈希表等等,最终他们选择了红黑树(rbtree)来组织hrtimer,红黑树已经以库的形式存在于内核中,
并被成功地使用在 内存管理子系统 和 文件系统中,
随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器,
内核用一个hrtimer结构来表示一个高精度定时器.
2. hrtimer如何运转
hrtimer的实现需要一定的硬件基础,它的实现依赖于我们前几章介绍的timekeeper和clock_event_device,
只要先知道,一旦开启了hrtimer,tick_device所关联的clock_event_device的事件回调函数会被修改为:hrtimer_interrupt,
并且会被设置成工作于CLOCK_EVT_MODE_ONESHOT单触发模式。
2.1 添加一个hrtimer
要添加一个hrtimer,系统提供了一些api供我们使用,首先我们需要定义一个hrtimer结构的实例,然后用hrtimer_init函数对它进行初始化,它的原型如下:
void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, enum hrtimer_mode mode);
//which_clock可以是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_BOOTTIME中的一种,
//mode则可以是相对时间HRTIMER_MODE_REL,也可以是绝对时间HRTIMER_MODE_ABS。
设定回调函数:
timer.function = hr_callback;
************************************************************************************************
如果定时器无需指定一个到期范围,可以在设定回调函数后, 直接使用hrtimer_start激活该定时器:
************************************************************************************************
int hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode);
************************************************************************************************
如果需要指定到期范围,则可以使用hrtimer_start_range_ns激活定时器:
************************************************************************************************
hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long range_ns, const enum hrtimer_mode mode);
************************************************************************************************
要取消一个hrtimer,使用hrtimer_cancel:
************************************************************************************************
int hrtimer_cancel(struct hrtimer *timer);
以下两个函数用于推后定时器的到期时间:
extern u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
/* 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);
}
以下几个函数用于获取定时器的当前状态:
static inline int hrtimer_active(const struct hrtimer *timer)
{
return timer->state != HRTIMER_STATE_INACTIVE;
}
static inline int hrtimer_is_queued(struct hrtimer *timer)
{
return timer->state & HRTIMER_STATE_ENQUEUED;
}
static inline int hrtimer_callback_running(struct hrtimer *timer)
{
return timer->state & HRTIMER_STATE_CALLBACK;
}
//----------------------------------------------------------------------------------------------