java 定时器 跳出定时器 java定时器暂停_java 定时器 跳出定时器


一、定时器

定时器在操作系统中地位很高,可以用来控制一切周期性行为。定时器采用中断的形式,工作原理如下图:


java 定时器 跳出定时器 java定时器暂停_java 定时器 跳出定时器_02


二、定时器优化

以前我在做单片机项目的时候,总感觉定时器中断数量太少(一般是两个),通过这次《30天》操作系统的学习,我才意识到不是定时器少,而是自己的编程思路有问题。理论上讲,你可以实现任何时间粒度的定时器中断编程,无非就是在基本时钟中断频率的基础上用倍数来获取任意时间间隔的问题。

由于中断程序对编程者要求很高,因此如果设计的中断程序不够合理,那么就会给计算机使用者带来严重的卡顿得现象。因此,该书中,对定时器中断不断的进行优化。过程如下:

1.基本定时器中断:通过对定时器芯片编程来获取1秒100次的基础中断。


void init_pit(void)
{
	io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
	return;
}


2.任意时间间隔定时器:通过倍数(timeout)来获取任意时间间隔,并在达到这个时间间隔值之后向缓冲区fifo写数据data。这样需要定义一个专门的时间定时器结构体:


struct TIMERCTL {
	unsigned int count;
	unsigned int timeout;
	struct FIFO8 *fifo;
	unsigned char data;
};


这里开始出现了一个变量:count。它是一个绝对的全局变量,也是定时器中断里面最重要的变量:因为它相当于时钟信号,自始至终不论刮风下雨都会按时count++。

此时,我们定义每一个定时器都需要对它进行初始化,以定义不同的时间间隔、缓冲区以及data,通过如下函数实现:


void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data)
{
	int eflags;
	eflags = io_load_eflags();
	io_cli();
	timerctl.timeout = timeout;
	timerctl.fifo = fifo;
	timerctl.data = data;
	io_store_eflags(eflags);
	return;
}


主程序可用extern struct TIMERCTL timerctl的方式来读取count值和读取缓冲区里面的数据.

3.多个定时器控制

一个定时器中断显然不够用,因此必须要定义各种不同时间间隔的定时器组成的数组,这样的话就定义两种结构体:第1种是每个定时器的结构体,通过timeout来控制时间倍数。第2种是管理所有定时器的结构体,来把所有的定时器以数组的方式放进去。


#define MAX_TIMER		500
struct TIMER {
	unsigned int timeout, flags;
	struct FIFO8 *fifo;
	unsigned char data;
};
struct TIMERCTL {
	unsigned int count;
	struct TIMER timer[MAX_TIMER];
};


这样一来,每次发生中断,我们需要在中断服务程序里面遍历所有的定时器看是否达到时间:


/void inthandler20()
{
	int i;
	io_out8(PIC0_OCW2, 0x60);	 
	timerctl.count++;   /*  1/100秒就发生的定时中断  */
	for (i = 0; i < MAX_TIMER; i++)   /*  遍历检查所有定时器是否达到时间  */
        {
		if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
			if (timerctl.timer[i].timeout <= timerctl.count) {
				timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
				fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
			}
		}
	}
	return;
}


4.定时器优化1---由一个专门指向当前最先即将到达时间的定时器指针来管理

上面的中断程序无效遍历太多了,会严重影响性能。因此我们优化成设计一个全局变量的next,用它来始终指向下一个最先会达到的定时器时间。它会通过如下的比较方式来确保每次新增加一个定时器时间的时候,始终指向的是最小的时间:


void timer_settime(struct TIMER *timer, unsigned int timeout)
{
	timer->timeout = timeout + timerctl.count;
	timer->flags = TIMER_FLAGS_USING;
	if (timerctl.next > timer->timeout) {
		/* 更新下一次最近时刻 */
		timerctl.next = timer->timeout;
	}
	return;
}


同时,每次一旦有某个定时器时间一到达,next自然又会遍历一遍所有定时器,取出最小的值


void inthandler20(int *esp)
{
	int i;
	io_out8(PIC0_OCW2, 0x60);	 
	timerctl.count++;
	if (timerctl.next > timerctl.count) {
		return; /* 没有定时器到时直接返回,节约中断时间 */
	}
	timerctl.next = 0xffffffff;
	for (i = 0; i < MAX_TIMER; i++) {
		if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
			if (timerctl.timer[i].timeout <= timerctl.count) {
				/* 现有最近的时间定时器到时了 */
				timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
				fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
			} else {
				/* 其他的定时器都没有到时,但是需要更新next指向下一个最近时间 */
				if (timerctl.next > timerctl.timer[i].timeout) {
					timerctl.next = timerctl.timer[i].timeout;
				}
			}
		}
	}
	return;
}


4.定时器优化2---用专门的数组指针来管理所有的定时器

上面的程序也存在问题,那就是当后定时器时间到时和没有定时器时间到时,程序处理的时间差异会很大,所以两种不同情况下,CPU反应时间不一致,可能会引起计算机在响应键盘和鼠标等时有明显的卡顿。另外,在有定时器时间达到的情况下,每次寻找next的过程中,又要遍历所有的定时器,这个无疑是不合理的。

所以,我们可以采用和“图层技术”相同的策略:用1个top值来表征当前所有的定时器个数(数组长度),同时用一个指针数组来指向定时器数组的对应位置。这样,一旦有定时器时间达到,就相当于之前有图层窗口关闭,我们就重新将所有的定时器整体往下压一个位置(移动),同时修改指针数组的指向,确保始终指向对应标号的定时器。所以,我们定义如下两种结构体:


#define MAX_TIMER		500
struct TIMER {
	unsigned int timeout, flags;
	struct FIFO8 *fifo;
	unsigned char data;
};
struct TIMERCTL {
	unsigned int count, next, using;
	struct TIMER *timers[MAX_TIMER];
	struct TIMER timers0[MAX_TIMER];
};


这里的using就相当于之前图层技术中的top值,相应的各个函数由于逻辑复杂,这里省略。

5.定时器优化3---把所有的定时器缓冲区全部统一,并和键盘鼠标的缓冲区一起。