7 加快中断处理(3)(harib09g)
到了harib09f的时候,中断处理程序的平均处理时间已经大大缩短了。这真是太好了。可是,现在有一个问题,那就是到达next时刻和没到next时刻的定时器中断,它们的处理时间差别很大。这样的程序结构不好。因为平常运行一直都很快的程序,会偶尔由于中断处理拖得太长,而搞得像是主程序要停了似的。更确切一点,这样有时会让人觉得“不知为什么,鼠标偶尔会反应迟钝,很卡。”
因此,我们要让到达next时刻的定时器中断的处理时间再缩短一些。嗯,怎么办呢?模仿sheet.c的做法怎么样呢?我们来试试看。
在sheet.c的结构体struct SHTCTL中,除了sheet0[ ]以外,我们还定义了*sheets[ ]。它里面存放的是按某种顺序排好的图层地址。有了这个变量,按顺序描绘图层就简单了。这次我们在Struct TIMERCTL中也定义一个变量,其中存放按某种顺序排好的定时器地址。
本次的bootpack.h节选
struct TIMERCTL {
unsigned int count, next, using;
struct TIMER *timers[MAX_TIMER];
struct TIMER timers0[MAX_TIMER];
};
变量using相当于struct SHTCTL中的top,它用于记录现在的定时器中有几个处于活动中。
■■■■■
改进后的inthandler20函数如下:
本次的timer.c节选
void inthandler20(int *esp)
{
int i, j;
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */
timerctl.count++;
if (timerctl.next > timerctl.count) {
return;
}
for (i = 0; i < timerctl.using; i++) {
/* timers的定时器都处于动作中,所以不确认flags */
if (timerctl.timers[i]->timeout > timerctl.count) {
break;
}
/* 超时*/
timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
}
/* 正好有i个定时器超时了。其余的进行移位。 */
timerctl.using -= i;
for (j = 0; j < timerctl.using; j++) {
timerctl.timers[j] = timerctl.timers[i + j];
}
if (timerctl.using > 0) {
timerctl.next = timerctl.timers[0]->timeout;
} else {
timerctl.next = 0xffffffff;
}
return;
}
这样,即使是在超时的情况下,也不用查找下一个next时刻,或者查找有没有别的定时器超时了,真不错。如果有很多的定时器都处于正在执行的状态,我们会担心定时器因移位而变慢,这放在以后再改进吧(从13.5节开始讨论)。
■■■■■
由于timerctl中的变量名改变了,所以其他地方也要随之修改。
void init_pit(void)
{
int i;
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0;
timerctl.next = 0xffffffff; /* 因为最初没有正在运行的定时器 */
timerctl.using = 0;
for (i = 0; i < MAX_TIMER; i++) {
timerctl.timers0[i].flags = 0; /* 未使用 */
}
return;
}
struct TIMER *timer_alloc(void)
{
int i;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timers0[i].flags == 0) {
timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;
return &timerctl.timers0[i];
}
}
return 0; /* 没找到 */
}
这两个函数比较简单,只是稍稍修改了一下变量名。
■■■■■
在timer_settime函数中,必须将timer注册到timers中去,而且要注册到正确的位置。如果在注册时发生中断的话可就麻烦了,所以我们要事先关闭中断。
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
int e, i, j;
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
e = io_load_eflags();
io_cli();
/* 搜索注册位置 */
for (i = 0; i < timerctl.using; i++) {
if (timerctl.timers[i]->timeout >= timer->timeout) {
break;
}
}
/* i号之后全部后移一位 */
for (j = timerctl.using; j > i; j--) {
timerctl.timers[j] = timerctl.timers[j - 1];
}
timerctl.using++;
/* 插入到空位上 */
timerctl.timers[i] = timer;
timerctl.next = timerctl.timers[0]->timeout;
io_store_eflags(e);
return;
}
这样做看来不错。虽然中断处理程序速度已经提高了,但在设定定时器期间,我们关闭了中断,这多少有些令人遗憾。不过就算对此不满意,也不要随便更改哦。
从某种程度上来讲,这也是无法避免的事。如果在设定时,多下点工夫整理一下,到达中断时刻时就能轻松一些了。反之,如果在设定时偷点懒,那么到达中断时刻时就要吃点苦头了。总之,要么提前做好准备,要么临时抱佛脚。究竟哪种做法好呢,要根据情况而定。如果是笔者的话会选择提前准备。也没有什么特殊的理由,只是笔者喜欢这样吧(笑)。
■■■■■
现在我们执行“make run”看看吧。希望它能正常运行。会怎么样呢?貌似很顺利,太好了。
关于定时器我们还有想要修改的地方。不过大家肯定已经很困了,我们还是明天再继续吧。再见!