7 加快中断处理(3)(harib09g)

到了harib09f的时候,中断处理程序的平均处理时间已经大大缩短了。这真是太好了。可是,现在有一个问题,那就是到达next时刻和没到next时刻的定时器中断,它们的处理时间差别很大。这样的程序结构不好。因为平常运行一直都很快的程序,会偶尔由于中断处理拖得太长,而搞得像是主程序要停了似的。更确切一点,这样有时会让人觉得“不知为什么,鼠标偶尔会反应迟钝,很卡。”

因此,我们要让到达next时刻的定时器中断的处理时间再缩短一些。嗯,怎么办呢?模仿sheet.c的做法怎么样呢?我们来试试看。

在sheet.c的结构体struct SHTCTL中,除了sheet0[ ]以外,我们还定义了*sheets[ ]。它里面存放的是按某种顺序排好的图层地址。有了这个变量,按顺序描绘图层就简单了。这次我们在Struct TIMERCTL中也定义一个变量,其中存放按某种顺序排好的定时器地址。

本次的bootpack.h节选

  1. struct TIMERCTL {
  2. unsigned int count, next, using;
  3. struct TIMER *timers[MAX_TIMER];
  4. struct TIMER timers0[MAX_TIMER];
  5. };

变量using相当于struct SHTCTL中的top,它用于记录现在的定时器中有几个处于活动中。

■■■■■

改进后的inthandler20函数如下:

本次的timer.c节选

  1. void inthandler20(int *esp)
  2. {
  3. int i, j;
  4. io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */
  5. timerctl.count++;
  6. if (timerctl.next > timerctl.count) {
  7. return;
  8. }
  9. for (i = 0; i < timerctl.using; i++) {
  10. /* timers的定时器都处于动作中,所以不确认flags */
  11. if (timerctl.timers[i]->timeout > timerctl.count) {
  12. break;
  13. }
  14. /* 超时*/
  15. timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
  16. fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
  17. }
  18. /* 正好有i个定时器超时了。其余的进行移位。 */
  19. timerctl.using -= i;
  20. for (j = 0; j < timerctl.using; j++) {
  21. timerctl.timers[j] = timerctl.timers[i + j];
  22. }
  23. if (timerctl.using > 0) {
  24. timerctl.next = timerctl.timers[0]->timeout;
  25. } else {
  26. timerctl.next = 0xffffffff;
  27. }
  28. return;
  29. }

这样,即使是在超时的情况下,也不用查找下一个next时刻,或者查找有没有别的定时器超时了,真不错。如果有很多的定时器都处于正在执行的状态,我们会担心定时器因移位而变慢,这放在以后再改进吧(从13.5节开始讨论)。

■■■■■

由于timerctl中的变量名改变了,所以其他地方也要随之修改。

  1. void init_pit(void)
  2. {
  3. int i;
  4. io_out8(PIT_CTRL, 0x34);
  5. io_out8(PIT_CNT0, 0x9c);
  6. io_out8(PIT_CNT0, 0x2e);
  7. timerctl.count = 0;
  8. timerctl.next = 0xffffffff; /* 因为最初没有正在运行的定时器 */
  9. timerctl.using = 0;
  10. for (i = 0; i < MAX_TIMER; i++) {
  11. timerctl.timers0[i].flags = 0; /* 未使用 */
  12. }
  13. return;
  14. }
  15. struct TIMER *timer_alloc(void)
  16. {
  17. int i;
  18. for (i = 0; i < MAX_TIMER; i++) {
  19. if (timerctl.timers0[i].flags == 0) {
  20. timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;
  21. return &timerctl.timers0[i];
  22. }
  23. }
  24. return 0; /* 没找到 */
  25. }

这两个函数比较简单,只是稍稍修改了一下变量名。

■■■■■

在timer_settime函数中,必须将timer注册到timers中去,而且要注册到正确的位置。如果在注册时发生中断的话可就麻烦了,所以我们要事先关闭中断。

  1. void timer_settime(struct TIMER *timer, unsigned int timeout)
  2. {
  3. int e, i, j;
  4. timer->timeout = timeout + timerctl.count;
  5. timer->flags = TIMER_FLAGS_USING;
  6. e = io_load_eflags();
  7. io_cli();
  8. /* 搜索注册位置 */
  9. for (i = 0; i < timerctl.using; i++) {
  10. if (timerctl.timers[i]->timeout >= timer->timeout) {
  11. break;
  12. }
  13. }
  14. /* i号之后全部后移一位 */
  15. for (j = timerctl.using; j > i; j--) {
  16. timerctl.timers[j] = timerctl.timers[j - 1];
  17. }
  18. timerctl.using++;
  19. /* 插入到空位上 */
  20. timerctl.timers[i] = timer;
  21. timerctl.next = timerctl.timers[0]->timeout;
  22. io_store_eflags(e);
  23. return;
  24. }

这样做看来不错。虽然中断处理程序速度已经提高了,但在设定定时器期间,我们关闭了中断,这多少有些令人遗憾。不过就算对此不满意,也不要随便更改哦。

从某种程度上来讲,这也是无法避免的事。如果在设定时,多下点工夫整理一下,到达中断时刻时就能轻松一些了。反之,如果在设定时偷点懒,那么到达中断时刻时就要吃点苦头了。总之,要么提前做好准备,要么临时抱佛脚。究竟哪种做法好呢,要根据情况而定。如果是笔者的话会选择提前准备。也没有什么特殊的理由,只是笔者喜欢这样吧(笑)。

■■■■■

现在我们执行“make run”看看吧。希望它能正常运行。会怎么样呢?貌似很顺利,太好了。

关于定时器我们还有想要修改的地方。不过大家肯定已经很困了,我们还是明天再继续吧。再见!