3 做个简单的多任务(1)(harib12c)
接下来,我们要实现更快速的,而且是来回交替的任务切换。这样一来,我们就可以告别光标停住、鼠标卡死、键盘打不了字等情况,从而让两个任务看上去好像在同时运行一样。
在开始动手之前,笔者认为像taskswitch3、taskswitch4这种写法实在不好。假设我们有100个任务,难道就要创建100个任务切换函数不成?这样肯定不行,最好是写成一个函数,比如像taskswitch(3);这样。
为了解决这个问题,我们先创建这样一个函数。
本次的naskfunc.nas节选
_farjmp: ; void farjmp(int eip, int cs);
JMP FAR [ESP+4] ; eip, cs
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节选
void HariMain(void)
{
(中略)
timer_ts = timer_alloc();
timer_init(timer_ts, &fifo, 2);
timer_settime(timer_ts, 2);
(中略)
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
io_stihlt();
} else {
i = fifo32_get(&fifo);
io_sti();
if (i == 2) {
farjmp(0, 4 * 8);
timer_settime(timer_ts, 2);
} else if (256 <= i && i <= 511) { /*键盘数据*/
(中略)
} else if (512 <= i && i <= 767) { /*鼠标数据*/
(中略)
} else if (i == 10) { /* 10秒计时器*/
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
} else if (i == 3) { /* 3秒计时器*/
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
} else if (i <= 1) { /*光标用计时器*/
(中略)
}
}
}
}
void task_b_main(void)
{
struct FIFO32 fifo;
struct TIMER *timer_ts;
int i, fifobuf[128];
fifo32_init(&fifo, 128, fifobuf);
timer_ts = timer_alloc();
timer_init(timer_ts, &fifo, 1);
timer_settime(timer_ts, 2);
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
io_stihlt();
} else {
i = fifo32_get(&fifo);
io_sti();
if (i == 1) { /*任务切换*/
farjmp(0, 3 * 8);
timer_settime(timer_ts, 2);
}
}
}
}
上面的代码应该没有什么难点,不过还是稍微解释一下吧。在每个任务中,当从farjmp返回的时候,我们都将计时器重新设定到0.02秒之后,以便让程序在返回0.02秒之后,再次执行任务切换。
■■■■■
好了,这样做是不是能像我们所设想的那样,让键盘和鼠标持续响应呢?我们来“make run”……不错,键盘打字、鼠标操作、光标闪烁,全都运行正常,完全没有卡住。我们成功了。
不过,我们真的成功了吗?感觉不是很靠谱啊,task_b_main到底有没有运行啊?嗯,下面我们想办法来确认一下。