3 做个简单的多任务(1)(harib12c)

接下来,我们要实现更快速的,而且是来回交替的任务切换。这样一来,我们就可以告别光标停住、鼠标卡死、键盘打不了字等情况,从而让两个任务看上去好像在同时运行一样。

在开始动手之前,笔者认为像taskswitch3、taskswitch4这种写法实在不好。假设我们有100个任务,难道就要创建100个任务切换函数不成?这样肯定不行,最好是写成一个函数,比如像taskswitch(3);这样。

为了解决这个问题,我们先创建这样一个函数。

本次的naskfunc.nas节选

  1. _farjmp: ; void farjmp(int eip, int cs);
  2. JMP FAR [ESP+4] ; eip, cs
  3. RET

“JMP FAR”指令的功能是执行far跳转。在JMP FAR指令中,可以指定一个内存地址,CPU会从指定的内存地址中读取4个字节的数据,并将其存入EIP寄存器,再继续读取2个字节的数据,并将其存入CS寄存器。当我们调用这个函数,比如farjmp(eip,cs);,在[ESP+4]这个位置就存放了eip的值,而[ESP+8]则存放了cs的值,这样就可以实现far跳转了。

因此我们需要将调用的部分改写如下:

taskswitch3(); → farjmp(0, 3 * 8);

taskswitch4(); → farjmp(0, 4 * 8);

■■■■■

现在我们来缩短切换的间隔。在任务A和任务B中,分别准备一个timer_ts变量,以便每隔0.02秒执行一次任务切换。这个变量名中的ts就是“task switch”的缩写,代表“任务切换计时器”的意思。

本次的bootpack.c节选

  1. void HariMain(void)
  2. {
  3. (中略)
  4. timer_ts = timer_alloc();
  5. timer_init(timer_ts, &fifo, 2);
  6. timer_settime(timer_ts, 2);
  7. (中略)
  8. for (;;) {
  9. io_cli();
  10. if (fifo32_status(&fifo) == 0) {
  11. io_stihlt();
  12. } else {
  13. i = fifo32_get(&fifo);
  14. io_sti();
  15. if (i == 2) {
  16. farjmp(0, 4 * 8);
  17. timer_settime(timer_ts, 2);
  18. } else if (256 <= i && i <= 511) { /*键盘数据*/
  19. (中略)
  20. } else if (512 <= i && i <= 767) { /*鼠标数据*/
  21. (中略)
  22. } else if (i == 10) { /* 10秒计时器*/
  23. putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
  24. } else if (i == 3) { /* 3秒计时器*/
  25. putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
  26. } else if (i <= 1) { /*光标用计时器*/
  27. (中略)
  28. }
  29. }
  30. }
  31. }
  32. void task_b_main(void)
  33. {
  34. struct FIFO32 fifo;
  35. struct TIMER *timer_ts;
  36. int i, fifobuf[128];
  37. fifo32_init(&fifo, 128, fifobuf);
  38. timer_ts = timer_alloc();
  39. timer_init(timer_ts, &fifo, 1);
  40. timer_settime(timer_ts, 2);
  41. for (;;) {
  42. io_cli();
  43. if (fifo32_status(&fifo) == 0) {
  44. io_stihlt();
  45. } else {
  46. i = fifo32_get(&fifo);
  47. io_sti();
  48. if (i == 1) { /*任务切换*/
  49. farjmp(0, 3 * 8);
  50. timer_settime(timer_ts, 2);
  51. }
  52. }
  53. }
  54. }

上面的代码应该没有什么难点,不过还是稍微解释一下吧。在每个任务中,当从farjmp返回的时候,我们都将计时器重新设定到0.02秒之后,以便让程序在返回0.02秒之后,再次执行任务切换。

■■■■■

好了,这样做是不是能像我们所设想的那样,让键盘和鼠标持续响应呢?我们来“make run”……不错,键盘打字、鼠标操作、光标闪烁,全都运行正常,完全没有卡住。我们成功了。

不过,我们真的成功了吗?感觉不是很靠谱啊,task_b_main到底有没有运行啊?嗯,下面我们想办法来确认一下。