来看一下linux kernel的定时器是如何设定的
timer.h
#ifndef _LINUX_TIMER_H
#define _LINUX_TIMER_H
/*
* DON'T CHANGE THESE!! Most of them are hardcoded into some assembly language
* as well as being defined here.
*/
/*
* The timers are:
*
* BLANK_TIMER console screen-saver timer
*
* BEEP_TIMER console beep timer
*
* RS_TIMER timer for the RS-232 ports
*
* SWAP_TIMER timer for the background pageout daemon
*
* HD_TIMER harddisk timer
*
* HD_TIMER2 (atdisk2 patches)
*
* FLOPPY_TIMER floppy disk timer (not used right now)
*
* SCSI_TIMER scsi.c timeout timer
*
* NET_TIMER tcp/ip timeout timer
*
* COPRO_TIMER 387 timeout for buggy hardware..
*
* QIC02_TAPE_TIMER timer for QIC-02 tape driver (it's not hardcoded)
*
* MCD_TIMER Mitsumi CD-ROM Timer
*
* GSCD_TIMER Goldstar CD-ROM Timer
*
*/
#define BLANK_TIMER 0
#define BEEP_TIMER 1
#define RS_TIMER 2
#define SWAP_TIMER 3
#define HD_TIMER 16
#define FLOPPY_TIMER 17
#define SCSI_TIMER 18
#define NET_TIMER 19
#define SOUND_TIMER 20
#define COPRO_TIMER 21
#define QIC02_TAPE_TIMER 22 /* hhb */
#define MCD_TIMER 23
#define HD_TIMER2 24
#define GSCD_TIMER 25
#define DIGI_TIMER 29
// 固定定时器
struct timer_struct {
unsigned long expires;
void (*fn)(void);
};
extern unsigned long timer_active;
extern struct timer_struct timer_table[32];
/*
* This is completely separate from the above, and is the
* "new and improved" way of handling timers more dynamically.
* Hopefully efficient and general enough for most things.
*
* The "hardcoded" timers above are still useful for well-
* defined problems, but the timer-list is probably better
* when you need multiple outstanding timers or similar.
*
* The "data" field is in case you want to use the same
* timeout function for several timeouts. You can use this
* to distinguish between the different invocations.
*/
struct timer_list {
struct timer_list *next;
struct timer_list *prev;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long);
};
extern void add_timer(struct timer_list * timer);
extern int del_timer(struct timer_list * timer);
extern void it_real_fn(unsigned long);
extern inline void init_timer(struct timer_list * timer)
{
timer->next = NULL;
timer->prev = NULL;
}
#endif
sched.c
unsigned long timer_active = 0; //32bit,每个Bit代表一个定时器节点是否有定时任务挂靠(true有效)
struct timer_struct timer_table[32]; // 在linux1.0之前的版本,很多数据结构是固定size的,无法扩容。从1.3开始引入链表、哈希表、二叉树等可以扩展的结构。本文是1.3版本,上下兼容。因此既固定写死的数组,也有用链表实现的。
/*
* The head for the timer-list has a "expires" field of MAX_UINT,
* and the sorting routine counts on this..
*/
static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
#define SLOW_BUT_DEBUGGING_TIMERS 0
void add_timer(struct timer_list * timer)
{
unsigned long flags;
struct timer_list *p;
#if SLOW_BUT_DEBUGGING_TIMERS
if (timer->next || timer->prev) {
printk("add_timer() called with non-zero list from %p\n",
__builtin_return_address(0));
return;
}
#endif
p = &timer_head;
save_flags(flags); // 保存中断屏蔽字
cli(); // 禁止中断控制器
do { /* 将timer加入timer_head链表的合适位置(timeout顺序为从小到大) */
p = p->next;
} while (timer->expires > p->expires);
timer->next = p;
timer->prev = p->prev;
p->prev = timer;
timer->prev->next = timer;
restore_flags(flags); // 恢复中断屏蔽字、使能中断控制器
}
int del_timer(struct timer_list * timer)
{
int ret = 0;
if (timer->next) { // 如果timer是timer_head链表的节点,则从链表的环取下
unsigned long flags;
struct timer_list * next;
save_flags(flags);
cli();
if ((next = timer->next) != NULL) {
(next->prev = timer->prev)->next = next;
timer->next = timer->prev = NULL;
ret = 1;
}
restore_flags(flags);
}
return ret;
}
static inline void run_timer_list(void)
{
struct timer_list * timer;
// 变量整条timer_head链表(双向循环有头链表),执行超时的定时器节点的callback函数
while ((timer = timer_head.next) != &timer_head && timer->expires <= jiffies) {
void (*fn)(unsigned long) = timer->function;
unsigned long data = timer->data;
timer->next->prev = timer->prev;
timer->prev->next = timer->next;
timer->next = timer->prev = NULL;
sti();
fn(data);
cli();
}
}
static inline void run_old_timers(void)
{
struct timer_struct *tp;
unsigned long mask;
// 变量定时器数组,执行超时的定时器的callback函数,并关闭该定时器(所有老定时器的设置,都是one shot,而不是period)
for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (tp->expires > jiffies)
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
}