1 闲置任务(harib14a)
今天一开始,请大家先回忆一下任务A的情形。在harib13e中,任务A下面的LEVEL中有任务B0~B2,因此FIFO为空时我们可以让任务A进入休眠状态。那么,如果我们并未启动任务B0~B2的话,任务A又将会如何呢?
首先,如果我们不对任务A进行任何改写,就按照它现在的样子进入休眠状态的话,大家想一想,会发生什么呢?一旦任务A休眠,mtask.c将自动寻找下层LEVEL中的任务,但由于这一次我们没有启动任务B0~B2,因此程序就找不到其他的任务而导致运行出现异常。
如果这样不行,那我们把休眠的部分再改回io_hlt();不就好了吗?这样一来,不但程序运行不会出现异常,还能省电呢,撒花!……话虽如此,但如果一个操作系统要根据下层LEVEL是否存在任务来改写程序的话,大家觉得靠谱吗?笔者认为,即使不改写程序,也能自动在适当的LEVEL运行适当的任务,这样的操作系统才是优秀的操作系统。
因此,一般情况下可以让任务休眠,但当所有LEVEL中都没有任务存在的时候,就需要HTL了。接下来我们就按照这个要求来改写mtask.c。
■■■■■
那么我们从task_sleep开始改起吧。且慢,我们确实可以这样来改,但是改写之后task_sleep将变得更加复杂,速度也会打折扣(说是会打折扣,其实应该也就是稍微慢一点点而已啦)。其实我们还有更好的方法。
如果“所有LEVEL中都没有任务”就会出问题,那我们只要避免这种情况发生不就可以了吗?这类似于我们写定时器的时候所采用的“卫兵”的思路。
本次的mtask.c节选
void task_idle(void)
{
for (;;) {
io_hlt();
}
}
我们创建这样一个任务,并把它一直放在最下层LEVEL中,大家觉得如何?“idle”(闲置)就是代表“不工作,空闲”的意思,这个任务的功能只是执行HTL。
这样一来,即便任务A进入休眠状态,系统也会自动切换到上面这个闲置任务,于是便开始执行HTL。当我们移动鼠标,FIFO中有数据写入的时候,任务A就会被唤醒,系统会自动切换到任务A继续工作。
综上所述,我们完全不需要对task_sleep等代码进行任何改动,只需在task_init中将这个闲置任务放在最下层LEVEL中就可以了。
本次的mtask.c节选
struct TASK *task_init(struct MEMMAN *memman)
{
struct TASK *task, *idle;
(中略)
idle = task_alloc();
idle->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;
idle->tss.eip = (int) &task_idle;
idle->tss.es = 1 * 8;
idle->tss.cs = 2 * 8;
idle->tss.ss = 1 * 8;
idle->tss.ds = 1 * 8;
idle->tss.fs = 1 * 8;
idle->tss.gs = 1 * 8;
task_run(idle, MAX_TASKLEVELS - 1, 1);
return task;
}
就是这样,很简单吧。
■■■■■
接着我们来测试一下,将HariMain改成下面这样。
本次的bootpack.c节选
void HariMain(void)
{
(中略)
/* sht_win_b */
for (i = 0; i < 3; i++) {
(中略)
*((int *) (task_b[i]->tss.esp + 4)) = (int) sht_win_b[i];
/* task_run(task_b[i], 2, i + 1); */ /*这里!*/
}
(中略)
}
也就是说,我们不让系统运行任务B0~B2,这样就只剩下任务A和task_idle了。当然,这样改只是不启动任务而已,窗口还是会照常显示的。
然后我们来“make run”,画面如下。
只有任务A的情况下也不会出现异常哦!