8 取消定时器(harib21h)

上一节我们所实现的定时器功能,其实有个问题,接下来我们来解决它。

这个问题是在应用程序结束之后发生的,请大家想象一下noodle.hrb结束之后的情形。应用程序设置了一个1秒的定时器,当定时器到达指定时间时会产生超时,并向任务发送事先设置的数据。问题是,如果这时应用程序已经结束了,定时器的数据就会被发送到命令行窗口,而命令行窗口肯定是一头雾水。

为了确认这个问题,我们在harib21g中运行noodle.hrb,按回车键或者其他任意键结束程序看看。大约1秒钟之后,命令行窗口中会自动出现一个神秘的字符。用鼠标按“×”关闭窗口之后,也会出现同样的现象。

8 取消定时器(harib21h) - 图1

noodle.hrb结束后出现的神秘字符(不是C哦)

要解决这个问题,我们需要取消待机中的定时器,这样一来,就可以在应用程序结束的同时取消定时器,问题也就迎刃而解了。

■■■■■

首先我们来编写用于取消指定定时器的函数。

本次的timer.c节选

  1. int timer_cancel(struct TIMER *timer)
  2. {
  3. int e;
  4. struct TIMER *t;
  5. e = io_load_eflags();
  6. io_cli(); /*在设置过程中禁止改变定时器状态*/
  7. if (timer->flags == TIMER_FLAGS_USING) { /*是否需要取消?*/
  8. if (timer == timerctl.t0) {
  9. /*第一个定时器的取消处理*/
  10. t = timer->next;
  11. timerctl.t0 = t;
  12. timerctl.next = t->timeout;
  13. } else {
  14. /*非第一个定时器的取消处理*/
  15. /*找到timer前一个定时器*/
  16. t = timerctl.t0;
  17. for (;;) {
  18. if (t->next == timer) {
  19. break;
  20. }
  21. t = t->next;
  22. }
  23. t->next = timer->next; /*将之前“timer的下一个”指向“timer的下一个”*/
  24. }
  25. timer->flags = TIMER_FLAGS_ALLOC;
  26. io_store_eflags(e);
  27. return 1; /*取消处理成功*/
  28. }
  29. io_store_eflags(e);
  30. return 0; /*不需要取消处理*/
  31. }

详细的解说已经写在程序的注释中了,请大家自行阅读。

接下来,我们来编写在应用程序结束时取消全部定时器的函数。在此之前,我们需要在定时器上增加一个标记,用来区分该定时器是否需要在应用程序结束时自动取消。如果没有这个标记的话,命令行窗口中用来控制光标闪烁的定时器也会被取消掉了。

本次的bootpack.h节选

  1. struct TIMER {
  2. struct TIMER *next;
  3. unsigned int timeout;
  4. char flags, flags2; /*这里!*/
  5. struct FIFO32 *fifo;
  6. int data;
  7. };

通常情况下,这里的flags2为0,为了避免忘记置0,我们来修改一下timer.alloc。

本次的timer.c节选

  1. struct TIMER *timer_alloc(void)
  2. {
  3. int i;
  4. for (i = 0; i < MAX_TIMER; i++) {
  5. if (timerctl.timers0[i].flags == 0) {
  6. timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;
  7. timerctl.timers0[i].flags2 = 0; /*这里!*/
  8. return &timerctl.timers0[i];
  9. }
  10. }
  11. return 0; /*没有找到*/
  12. }

接下来,我们将应用程序所申请的定时器的flags2设为1。

本次的console.c节选

  1. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  2. {
  3. (中略)
  4. } else if (edx == 16) {
  5. reg[7] = (int) timer_alloc();
  6. ((struct TIMER *) reg[7])->flags2 = 1; /*允许自动取消*/ /*这里!*/
  7. } else if (edx == 17) {
  8. (中略)
  9. }

准备完成了,下面我们就编写一个函数,来取消应用程序结束时所不需要的定时器。

本次的console.c节选

  1. int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
  2. {
  3. (中略)
  4. if (finfo != 0) {
  5. (中略)
  6. if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
  7. (中略)
  8. start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
  9. shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
  10. for (i = 0; i < MAX_SHEETS; i++) {
  11. sht = &(shtctl->sheets0[i]);
  12. if ((sht->flags & 0x11) == 0x11 && sht->task == task) {
  13. /*找到应用程序残留的窗口*/
  14. sheet_free(sht); /*关闭*/
  15. }
  16. }
  17. timer_cancelall(&task->fifo); /*这里!*/
  18. memman_free_4k(memman, (int) q, segsiz);
  19. } else {
  20. (中略)
  21. }
  22. (中略)
  23. }
  24. (中略)
  25. }

本次的timer.c节选

  1. void timer_cancelall(struct FIFO32 *fifo)
  2. {
  3. int e, i;
  4. struct TIMER *t;
  5. e = io_load_eflags();
  6. io_cli(); /*在设置过程中禁止改变定时器状态*/
  7. for (i = 0; i < MAX_TIMER; i++) {
  8. t = &timerctl.timers0[i];
  9. if (t->flags != 0 && t->flags2 != 0 && t->fifo == fifo) {
  10. timer_cancel(t);
  11. timer_free(t);
  12. }
  13. }
  14. io_store_eflags(e);
  15. return;
  16. }

大功告成了!

我们来“make run”,运行noodle.hrb,然后让程序结束。哦哦成功了,神秘字符再也不出现了,太好了。

8 取消定时器(harib21h) - 图2

神秘字符消失了

好了,今天的内容就到这里吧,明天见哦!