Linux-0.11中的内核定时器它是一个软定时器,还是由jiffies来实现的,最多同时可支持64个内核定时器,内核定时器数据结构定义如下:
如果fn为NULL,则直接返回。否则先关中断,检测jiffies值,如果小于等于0,则直接执行fn指针函数,那么它是不会被加入到定时器链表中的。
否则进入else语句中。首先在定时链表数组中找到一个空的位置,如果没有空的位置,即内核中同时存在64个内核定时器了,则系统会崩溃掉。。。不用担心,现在是不会出现这个结果的,也只有软盘那个地方才用到了内核定时器。那么接下来是往内核添加一个定时器,我们分几种情况来看:
1. 内核无定时器
初始情况下,即内核无任何内核定时器,next_timer为NULL,然后是对内核定时器的一些赋值操作,内核定时器的next指针赋值为next_timer(为NULL),而next_timer则指向了新增的内核定时器p,由于p->next的值为NULL,那么while中的语句是不会被执行的,这部分图例如下。
2. 存在一个或多个定时器的情况下
紧接着上面,假设存在一个定时器,再往内核添加定时器时,新增的内核定时器p的next指针等于next_timer,而next_timer指向了第一个定时器,所以,新增的定时器next指针指向第一个定时器,而next_timer指针则指向新增的定时器,图例如下。
那么现在新增的定时器的next指针不在为NULL了,它指向了第一个定时器节点,如果新增的定时器的jiffies值大于第一个定时器的jiffies值,那么while循环中的语句被执行。在while语句中实际上是遍历定时器链表,将jiffies值最小的排在定时器链表的开始处,jiffies值最大的排在最后,同时后面的定时器节点的jiffies值会减去前面所有定时器jiffies值的和,那么这里为什么会减去这个值呢?在do_timer函数中再来分析。
do_timer函数关于定时器部分代码如下:
注意在前面do_timer函数中,只对next_timer所指向的定时器的jiffies值做了减1操作,而其它定时器的jiffies值并未做减1操作,即只有当前一个定时器的jiffies值减为零了才会对下一个定时器的jiffies值做减1操作。也就不难为理解了,在前面的add_timer函数的while语句中,在往后移动定时器节点时为什么会将其jiffies值减去见面的定时器节点的jiffies值的原因了。
从上面可以总结出,定时器链表的第一个节点是jiffies值最小的那个节点,最后面是jiffies值最大的那个。但是前面的add_timer函数处似乎没有考虑周全,即如果新增的的定时器的jiffies值如果小于后面的定时器的jiffies值,那么while语句是不会被执行的,而强行加在了链表的开始处,而后面的定时器的jiffies并做相应的减操作,即后面所有的定时器都会延后到期。
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
jiffies是定时器的到期时间,fn是定时器到期后将要执行的函数指针。
往内核添加定时器使用add_timer函数,代码如下:
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
如果fn为NULL,则直接返回。否则先关中断,检测jiffies值,如果小于等于0,则直接执行fn指针函数,那么它是不会被加入到定时器链表中的。
否则进入else语句中。首先在定时链表数组中找到一个空的位置,如果没有空的位置,即内核中同时存在64个内核定时器了,则系统会崩溃掉。。。不用担心,现在是不会出现这个结果的,也只有软盘那个地方才用到了内核定时器。那么接下来是往内核添加一个定时器,我们分几种情况来看:
1. 内核无定时器
初始情况下,即内核无任何内核定时器,next_timer为NULL,然后是对内核定时器的一些赋值操作,内核定时器的next指针赋值为next_timer(为NULL),而next_timer则指向了新增的内核定时器p,由于p->next的值为NULL,那么while中的语句是不会被执行的,这部分图例如下。
next_timer--->[0]--->NULL
2. 存在一个或多个定时器的情况下
紧接着上面,假设存在一个定时器,再往内核添加定时器时,新增的内核定时器p的next指针等于next_timer,而next_timer指向了第一个定时器,所以,新增的定时器next指针指向第一个定时器,而next_timer指针则指向新增的定时器,图例如下。
next_timer--->[1]--->[0]--->NULL从上面可以看出,next_timer实际上是链表的一个头指针,而往链表新增一个节点实际上是在链表头部添加的。
那么现在新增的定时器的next指针不在为NULL了,它指向了第一个定时器节点,如果新增的定时器的jiffies值大于第一个定时器的jiffies值,那么while循环中的语句被执行。在while语句中实际上是遍历定时器链表,将jiffies值最小的排在定时器链表的开始处,jiffies值最大的排在最后,同时后面的定时器节点的jiffies值会减去前面所有定时器jiffies值的和,那么这里为什么会减去这个值呢?在do_timer函数中再来分析。
do_timer函数关于定时器部分代码如下:
void do_timer(long cpl)
{
/* ... */
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
/* ... */
}
假设定时器链表情况如下:
next_timer--->[1]--->[0]--->NULLnext_timer所指向的那个节点肯定是jiffies值最小的那个,应该是最先到期的,所以一个定时器中断来了之后jiffies值减1,如果jiffies值已经为0,即已经到期了,那么将next_timer指向下一个节点,同时调用已经到期的那个定时器的fn函数指针,如图:
next_timer--->[0]--->NULL
注意在前面do_timer函数中,只对next_timer所指向的定时器的jiffies值做了减1操作,而其它定时器的jiffies值并未做减1操作,即只有当前一个定时器的jiffies值减为零了才会对下一个定时器的jiffies值做减1操作。也就不难为理解了,在前面的add_timer函数的while语句中,在往后移动定时器节点时为什么会将其jiffies值减去见面的定时器节点的jiffies值的原因了。
从上面可以总结出,定时器链表的第一个节点是jiffies值最小的那个节点,最后面是jiffies值最大的那个。但是前面的add_timer函数处似乎没有考虑周全,即如果新增的的定时器的jiffies值如果小于后面的定时器的jiffies值,那么while语句是不会被执行的,而强行加在了链表的开始处,而后面的定时器的jiffies并做相应的减操作,即后面所有的定时器都会延后到期。