【转】深入剖析linux内核的定时器实现机制-动态刷新维护
刷新定时器队列。首先,判断index是否为0,如果为0则需要从tv2中补充定时器到tv1中来。若tv2已经处理完最后一列向量,即 (base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK) == 0,则需要从tv3补充,依次类推。若tv2中有未处理完的,则将部分补充到tv1中,此时tv3、tv4、tv5都不需要更新。注 意&&的执行关系,前面为假后后面就不再执行。
² 执行到期的timer。list_replace_init获取当前待处理的向量列。将头保存到head中,删除所有定时器。while (!list_empty(head))循环若有到期的timer则获得待执行的函数地址及其参数,调用detach_timer从head中删除当前 timer。依次执行head链中所有的timer。
5 内核定时器的API
5.1 初始化
5.1.1 静态初始化
20extern struct tvec_t_base_s boot_tvec_bases;
21
22#define TIMER_INITIALIZER(_function, _expires, _data) { \
23 .function = (_function), \
24 .expires = (_expires), \
25 .data = (_data), \
26 .base = &boot_tvec_bases, \
27 }
TIMER_INITIALIZER构造一个内核timer元素,当其他结构体中包含一个内核timer时,此宏可以直接内嵌在结构体中。Linux内核 中常见的数据结构都采用了此方法,如自旋锁,等待队列等。
29#define DEFINE_TIMER(_name, _function, _expires, _data) \
30 struct timer_list _name = \
31 TIMER_INITIALIZER(_function, _expires, _data)
静态定义一个内核timer变量,同时对各个元素进行初始化。好处在于用户无需知道定时器的实现细节,同时可以防止用户忘记初始化导致的问题。
5.1.2 动态初始化init_timer
33void fastcall init_timer(struct timer_list * timer);
//////////////////////////////
#define fastcall __attribute__((regparm(3))) //通过寄存器传递参数可以提高性能,内核中的大部分函数都有此修饰符
#define asmlinkage __attribute__((regparm(0))) 函数定义前加宏asmlinkage,表示这些函数通过堆栈而不是通过寄存器传递参数。
gcc编译器在汇编过程中调用c语言函数时传递参数有两种方法:一种是 通过堆栈,另一种是通过寄存器。缺省时采用寄存器,假如你要在你的汇编过程中调用c语言函数,并且想通过堆栈传递参数,你定义的c函数时要在函数前加上宏 asmlinkage
//////////////////////////////
内核函数init_timer()用来初始化一个定时器。实际上,这个初始化函数仅仅将结构中的list成员初始化为空并获得对应CPU上的base。如 下所示(/kernel/timer.c): 140void fastcall init_timer(struct timer_list *timer)
141{
142 timer->entry.next = NULL;
143 timer->base = __raw_get_cpu_var(tvec_bases);
144}
145EXPORT_SYMBOL(init_timer);
5.1.3 完全初始化setup_timer
34
35static inline void setup_timer(struct timer_list * timer,
36 void (*function)(unsigned long),
37 unsigned long data)
38{
39 timer->function = function;
40 timer->data = data;
41 init_timer(timer);
42}
动态初始化一个内核定时器,经过setup_timer的调用后,内核timer的各个域都有初始值了;该函数也可以对已经初始化的定时器重新初始化。
2.4的内核代码中还没有setup_timer函数,通常动态初始化时先用init_timer,接着对function域及data域赋值。这样用户 直接操作结构体成员变量,若内核timer结构改变,则可移植性就降低了。因此2.6内核通过DEFINE_TIMER及setup_timer对内核定 时器的初始化进行了全面封装,用户无需知道内核的实现细节,只需要按照内核timer的API编程即可,接口是固定的,内部实现细节的变化对用户程序 影响最小。
5.2 内部实现细节
5.2.1 时间比较
在定时器应用中经常需要比较两个时间值,以确定timer是否超时,所以Linux内核在timer.h头文件中定义了4个时间关系比较操作宏。这里我们 说时刻a在时刻b之后,就意味着时间值a≥b。Linux强烈推荐用户使用它所定义的下列4个时间比较操作宏(include/linux /jiffies.h),还是一句话,封装可以改善移植性:
106#define time_after(a,b) \
107 (typecheck(unsigned long, a) && \
108 typecheck(unsigned long, b) && \
109 ((long)(b) - (long)(a)
110#define time_before(a,b) time_after(b,a)
111
112#define time_after_eq(a,b) \
113 (typecheck(unsigned long, a) && \
114 typecheck(unsigned long, b) && \
115 ((long)(a) - (long)(b) >= 0))
116#define time_before_eq(a,b) time_after_eq(b,a)
5.2.2 挂起判断
由于定时器通常被连接在一个双向循环队列中等待执行,此时我们说定时器处于pending状态。因此函数time_pending()就可以用entry 成员是否为空来判断一个定时器是否处于pending状态。如下所示 (include/linux/timer.h): 44/***
Callers must ensure serialization wrt. other operations done to this timer, eg. interrupt contexts, or other CPUs on SMP.
return value: 1 if the timer is pending, 0 if not.
53 */
54static inline int timer_pending(const struct timer_list * timer)
55{
56 return timer->entry.next != NULL;
57}
5.2.3 锁定定时器base
158/*
159 * We are using hashed locking: holding per_cpu(tvec_bases).lock
160 * means that all timers which are tied to this base via timer->base are
161 * locked, and the base itself is locked too.
166 * When the timer's base is locked, and the timer removed from list, it is
167 * possible to set timer->base = NULL and drop the lock: the timer remains
168 * locked.
169 */
170static tvec_base_t *lock_timer_base(struct timer_list *timer,
171 unsigned long *flags)
172 __acquires(timer->base->lock)
173{
174 tvec_base_t *base;
175
176 for (;;) {
177 base = timer->base;
178 if (likely(base != NULL)) {
179 spin_lock_irqsave(&base->lock, *flags);
180 if (likely(base == timer->base))
181 return base;
182 /* The timer has migrated to another CPU */
183 spin_unlock_irqrestore(&base->lock, *flags);
184 }
185 cpu_relax();
186 }
187}
5.2.4 内部删除
函数detach_timer()如下所示(kernel/timer.c):
147static inline void detach_timer(struct timer_list *timer,
148 int clear_pending)
149{
150 struct list_head *entry = &timer->entry;
151
152 __list_del(entry->prev, entry->next);
153 if (clear_pending)
154 entry->next = NULL;
155 entry->prev = LIST_POISON2; //非0的防范值,操作时将导致页表异常
156}
函数detach_timer()用来将一个定时器从相应的内核定时器队列中删除。前提条件,该定时器处于定时器队列中。首先将起从链表中删除,根据 clear_pending标志是否清除entry->next。
5.2.5 内部修部timer值
189int __mod_timer(struct timer_list *timer, unsigned long expires)
190{
191 tvec_base_t *base, *new_base;
192 unsigned long flags;
193 int ret = 0;
194
195 BUG_ON(!timer->function);
////
#define BUG_ON(condition) do { if (condition) ; } while(0)
/////
196
197 base = lock_timer_base(timer, &flags); // 获得timer所在的根base,同时锁定
198
199 if (timer_pending(timer)) {
200 detach_timer(timer, 0);
201 ret = 1;
202 }
203
204 new_base = __get_cpu_var(tvec_bases);
205
206 if (base != new_base) {
207 /*
208 * We are trying to schedule the timer on the local CPU.
213 */
214 if (likely(base->running_timer != timer)) {
215 /* See the comment in lock_timer_base() */
216 timer->base = NULL;
217 spin_unlock(&base->lock);
218 base = new_base;
219 spin_lock(&base->lock);
220 timer->base = base;
221 }
222 }
223
224 timer->expires = expires;
225 internal_add_timer(base, timer);
226 spin_unlock_irqrestore(&base->lock, flags);
227
228 return ret;
229}
230
231EXPORT_SYMBOL(__mod_timer);
该函数首先判断回调函数是否设置否则返回。然后获得timer所在的根base,判断是否已经是否已经添加,若已经添加则调用 detach_timer()函数将该定时器从它原来所属的链表中删除。接着获得更新的timer所在的根base,最后调用 internal_add_timer()函数将该定时器根据它新的expires值重新插入到相应的链表中。
5.3 添加定时器到内核中add_timer
函数add_timer()用来将参数timer指针所指向的定时器插入到一个合适的定时器链表中。它首先调用timer_pending()函数判断所 指定的定时器是否已经位于在某个定时器向量中等待执行。如果是,则不进行任何操作,只是打印一条内核告警信息就返回了;如果不是,则调用 __mod_timer函数完成实际的插入操作。其源码如下(kernel/timer.h):
static inline void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
__mod_timer(timer, timer->expires);
}
对于add_timer来说,if (timer_pending(timer))判断无意义,但对于mod_timer来说此句有意义,为了最大限度的代码复用,基本上 __mod_timer实现了所有timer的更改。add_timer只是mod_timer的一个特例而已,无需删除原来的链而已,但都需要插入到新 的队列中。
5.4 修改定时器的expires值mod_timer
当一个定时器已经被插入到内核动态定时器链表中后,我们还可以修改该定时器的expires值。函数mod_timer()实现这一点。如下所示 (kernel/timer.c):
253/**
258 * mod_timer is a more efficient way to update the expire field of an
259 * active timer (if the timer is inactive it will be activated)
260 *
261 * mod_timer(timer, expires) is equivalent to:
262 *
263 * del_timer(timer); timer->expires = expires; add_timer(timer);
264 *
265 * Note that if there are multiple unserialized concurrent users of the
266 * same timer, then mod_timer() is the only safe way to modify the timeout,
267 * since add_timer() cannot modify an already running timer.
272 */
273int mod_timer(struct timer_list *timer, unsigned long expires)
274{
275 BUG_ON(!timer->function);
276
277 /*
278 * 如果定时器修改为自身,则直接返回,避免无谓的修改,很多地方需要用 279 到这个再浅显不过的道理
281 */
282 if (timer->expires == expires && timer_pending(timer))
283 return 1;
284
285 return __mod_timer(timer, expires);
286}
287
288EXPORT_SYMBOL(mod_timer);
实际上该函数有以下功能:
² 已经激活的timer,即timer_pending返回1,若修改值为自身,则直接返回;
² 已经激活的timer,真正的修改其值,则修改,函数本意;
² 未激活的timer,无论timer->expires多少,都添加到队列中,激活了timer,此时相当于add_timer。
5.5 删除一个定时器del_timer
与2.4内核下的意义有点差别。detach_timer是内核内部调用的函数,而del _timer是对外的API。首先调用timer_pending()来判断指定的定时器是否已经处于某个链表中,如果定时器原来就不处于任何链表中,则 del _timer()函数什么也不做,直接返回0值,表示失败。否则,就调用detach_timer函数将定时器从它原来所处的链表中摘除,返回1成功。如 下所示(kernel/timer.c):
290/**
291 * del_timer - deactive a timer.
292 * @timer: the timer to be deactivated
293 *
294 * del_timer() deactivates a timer - this works on both active and inactive
295 * timers.
300 */
301int del_timer(struct timer_list *timer)
302{
303 tvec_base_t *base;
304 unsigned long flags;
305 int ret = 0;
306
307 if (timer_pending(timer)) {
308 base = lock_timer_base(timer, &flags);
309 if (timer_pending(timer)) {
310 detach_timer(timer, 1);
311 ret = 1;
312 }
313 spin_unlock_irqrestore(&base->lock, flags);
314 }
315
316 return ret;
317}
318
319EXPORT_SYMBOL(del_timer);
上述函数中的宏TVN_SIZE是指timer_vec结构类型中的定时器向量指针数组vec[]的大小,值为64。宏TVR_SIZE是指 timer_vec_root结构类型中的定时器向量数组vec[]的大小,值为256。 内核 ///////////////////////////////////////////////
在include/linux/timer.h头文件中定义了数据结构timer_list来描述一个内核定时器: 10struct timer_list {