7 增加命令行窗口(3)(harib22g)

为什么程序会无法正常关闭呢?一开始笔者以为问题出在关闭窗口的函数,或者是处理程序结束的部分,但事实并非如此,这个失误比想象中更加严重。问题的原因在于应用程序的内存段消失了,突然间竟然发生这种事情,QEMU肯定也被整糊涂了。

也许大家不明白应用程序的内存段消失是怎么一回事,总之,问题出在cmd_app身上。

harib22f的cmd_app节选

  1. set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
  2. set_segmdesc(gdt + 1004, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
  3. (中略)
  4. start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));

上面这段代码是用来创建应用程序段并启动应用程序的,大家仔细思考一下这段代码。

首先,color.hrb在某个窗口中被运行,启动程序一切顺利,然后显示窗口并绘图,接下来等待键盘输入并进入休眠状态。到这里为止没有任何问题。

然后我们在另外一个窗口中运行color.hrb,程序也顺利启动了,显示窗口并绘图,随后进入休眠状态。然而在这个时候,问题其实已经发生了。这是怎么回事呢?因为我们为color.hrb准备的1003号代码段和1004号数据段,被color2.hrb所用的段给覆盖掉了。

因此,当按下回车键唤醒color.hrb时,就会发生异常情况——明明应该去运行color.hrb的,结果却错误地运行了color2.hrb,这样当然会出错了。

■■■■■

既然问题的原因想明白了,要干掉这个bug也就不难了,只要为color.hrb和color2.hrb分配编号不同的段就可以了。

本次的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. set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
  9. set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
  10. (中略)
  11. start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0));
  12. (中略)
  13. } else {
  14. (中略)
  15. }
  16. (中略)
  17. }
  18. (中略)
  19. }

在task—>sel中填入TSS的段号 * 8(请参照mtask.c的task_init),将这个值除以8,结果一定落在3~1002。将其加上1000,就得到1003~2002的值,我们把它用作应用程序用的代码段编号;将其加上2000,即得到2003~3002的值,我们把它用作应用程序用的数据段编号。这样一来,就不会发生段被覆盖的问题了。

■■■■■

我们来试试看能不能成功,“make run”,运行color.hrb和color2.hrb,并排对比一下,然后按下回车键结束程序。啊,这次终于成功了,撒花!

7 增加命令行窗口(3)(harib22g) - 图1

成功结束了应用程序