Linux-0.11 内核定时器

Linux-0.11中的内核定时器它是一个软定时器,还是由jiffies来实现的,最多同时可支持64个内核定时器,内核定时器数据结构定义如下:
#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]--->NULL
next_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并做相应的减操作,即后面所有的定时器都会延后到期。

你可能感兴趣的:(Linux-0.11 内核定时器)