【转】深入剖析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 {