第26天
前一章中我们做的操作系统已经越来越像超作系统了,我们同时打开了2个console窗口,并且把task_a的窗口给了消了。现在我们在启动时显打开一个console窗口,然后用户自己操作打开另一个窗口。我们就这么规定,如果按下shift+f2那么就打开一个命令行窗口。
为了方便我们将把开命令行窗口单独作成一个函数。
struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
struct SHEET *sht = sheet_alloc(shtctl);
unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
struct TASK *task = task_alloc();
int *cons_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
sheet_setbuf(sht, buf, 256, 165, -1);
make_window8(buf, 256, 165, "console", 0);
make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
task->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;
task->tss.eip = (int) &console_task;
task->tss.es = 1 * 8;
task->tss.cs = 2 * 8;
task->tss.ss = 1 * 8;
task->tss.ds = 1 * 8;
task->tss.fs = 1 * 8;
task->tss.gs = 1 * 8;
*((int *) (task->tss.esp + 4)) = (int) sht;
*((int *) (task->tss.esp + 8)) = memtotal;
task_run(task, 2, 2); /* level=2, priority=2 */
sht->task = task;
sht->flags |= 0x20; /* 有光标 */
fifo32_init(&task->fifo, 128, cons_fifo, task);
return sht;
}
这个函数返回一个SHEET结构体指针。只要我们在主函数中实现输入shift + f2就调用定个函数就可以了。
if (i == 256 + 0x3c && key_shift != 0 && sht_cons[1] == 0) { /* Shift+F2 */
sht_cons[1] = open_console(shtctl, memtotal);
sheet_slide(sht_cons[1], 32, 4);
sheet_updown(sht_cons[1], shtctl->top);
/* �自动将焦点切换到新打开的命令行窗口 */
keywin_off(key_win);
key_win = sht_cons[1];
keywin_on(key_win);
}
看上面的代码知道,目前我们操作系统还只支持最多打开2个命令行窗口。我们接下来想办法能打开更多的命令行窗口。实际上如果我们不用sht_cons[]这个数组保存命令行窗口,那么只要不关闭命令行窗口也不会出现问题。如果要关闭窗口我们就要考虑保存将命令行窗口所对应用栈内存和消息队列内存给释放,还要把窗口对应用图层内存释放。
首先我们将打开命令行窗口时申请的栈空间保存到TASK结构体中。
struct TASK {
int sel, flags;
int level, priority;
struct FIFO32 fifo;
struct TSS32 tss;
struct CONSOLE *cons;
int ds_base, cons_stack;
};
然后在打开窗口的函数中将栈地址保存起来
task->cons_stack = memman_alloc_4k(memman, 64 * 1024);
task->tss.esp = task->cons_stack + 64 * 1024 - 12;
再来写关闭命令行窗口的函数
void close_constask(struct TASK *task)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
task_sleep(task);
memman_free_4k(memman, task->cons_stack, 64 * 1024);
memman_free_4k(memman, (int) task->fifo.buf, 128 * 4);
task->flags = 0;
return;
}
void close_console(struct SHEET *sht)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
struct TASK *task = sht->task;
memman_free_4k(memman, (int) sht->buf, 256 * 165);
sheet_free(sht);
close_constask(task);
return;
}
接下来我们在命令行窗口中增加exit命令,如果运行这个命令那么命令行窗口就会关闭。
else if (strcmp(cmdline, "exit") == 0) {
cmd_exit(cons, fat);
}
当用户在命令行窗口中输入exit字符串的时候,操作系统执行cmd_exit函数。
void cmd_exit(struct CONSOLE *cons, int *fat)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
struct TASK *task = task_now();
struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
timer_cancel(cons->timer);
memman_free_4k(memman, (int) fat, 4 * 2880);
io_cli();
fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768); /* 768�~1023 */
io_sti();
for (;;) {
task_sleep(task);
}
}
我们首先取消了光标定时器,然后将FAT用的内存空间释放,然后我们向task_a任务发送消息,消息数据为图层号 + 768。
else if (768 <= i && i <= 1023) { /*命令行窗口关闭处理� */
close_console(shtctl->sheets0 + (i - 768));
}
task_a任务中的 i - 768 表示图层号。图层号的值加上shtctl->sheets0就是命令行窗口所对应的图层。
既然已经实现了用exit命令关闭命令行窗口那么我们也可以实现用鼠标点击关闭按钮关闭命令行窗口了。就像之前的关闭应用程序一样。回想起来,之前我们实现关闭应用程序窗口时候做了个判断。
else { /* 命令行窗口 */
task = sht->task;
io_cli();
fifo32_put(&task->fifo, 4);
io_sti();
}
如果点击关闭按钮那么我们向命令行窗口发送4这个数据,命令行窗口接收到4就调用cmd_exit函数,那么接下来就和之关闭的流程一样了。
这一章中再实现2个命令start 和ncst。start命令表示打开一个新的命令行窗口然后再在新的命令行窗口中运行start 后面跟着的应用程序。ncst命令不显示命令行窗口,但是运行一个新的应用程序。
start命令相对比较简单
else if(strncmp(cmdline, "start ", 6) == 0){
cmd_start(cons, cmdline, memtotal);
}
void cmd_start(struct CONSOLE * cons, char *cmdline, int memtotal){
struct SHTCTL *shtctl = (struct SHTCTL*) *((int *)0xofe4);
struct SHEET *sht = open_console(shtctl, memtotal);
struct FIFO32 *fifo = &sht->task->fif0;
int i;
sheet_slide(sht, 32, 4);
sheet_updown(sht, shtctl->top);
for(i = 6; cmdline[i] != 0; i++){
fifo32_put(fifo, cmdline[i] + 256);
}
fifo32_put(fifo, 10 + 256);
cons_newline(cons);
return;
}
ncst命令比较难。基本思路是:我们新建一个命令行的任务,但是不在屏幕上显示窗口。运行命令行的任务之后马上运行ncst命令之后跟着的应用程序。
首先添加这个命令:
else if(strncmp(cmdline, "ncst ", 5) == 0){
cmd_ncst(cons, cmdline, memtotal);
}
接下来实现cmd_ncst函数:
void cmd_ncst(struct CONSOLE *cons, char *cmdline, int memtotal){
struct TASK *task = open_constask(0, memtotal);
struct FIFO32 *fifo = &task->fifo;
int i;
for(i = 5; cmdline[i] != 0; i++){
fifo32_put(fifo, cmdline[i] + 256);
}
fifo32_put(fifo, 10+256);
cons_newline(cons);
return;
}
由于现在出现了不同的情况,所有的cons对应用函数都应该判断这个命令行是否有窗口,如果没有窗口,那么cons->sht应该为0。还要修改console_task函数,也就是命令行的主程序。如果没有窗口也就不需要设置光标闪烁的定时器了。cmd_exit函数也要修改,如果没有窗口关闭命令行的时候应该向task_a任务发送1024 + 任务号的消息。接下来实现open_constask函数。实际上之前的open_console已经实现了打开命令行的所有需要做的操作,我们现在把这个函数中创建任务的部分抽离出来。
struct TASK* open_constask(struct *sht, unsigned int memtotal){
struct MEMMAN *memman = (struct MEMMAN*) MEMMAN_ADDR;
struct TASK *task = task_alloc();
int *cons_fifo = (int *)memman_alloc_4k(memman, 128 * 4);
task->cons_stack = memman_alloc_4l(memman, 64 * 1024);
task->tss.esp = task->cons_stack + 64 * 1024 - 12;
task->tss.eip = (int) &console_task;
task->tss.es = 1 * 8;
task->tss.cs = 2 * 8;
task->tss.ss = 1 * 8;
task->tss.ds = 1 * 8;
task->tss.fs = 1 * 8;
task->tss.gs = 1 * 8;
*((int *)(task->tss.esp + 4)) = (int)sht;
*((int *)(task->tss.esp + 4)) = memtotal;
task_run(task, 2, 2);
fifo32_init(&task->fifo, 128, cons_fifo, task);
}
struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
struct SHEET *sht = sheet_alloc(shtctl);
unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
sheet_setbuf(sht, buf, 256, 165, -1);
make_window8(buf, 256, 165, "console", 0);
make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
sht->task = open_constask(sht, memtotal);
sht->flags |= 0x20;
return sht;
}
最后修改task_a任务中关于消息处理的部分。
else if (768 <= i && i <= 1023) { /* 命令行窗口关闭处理 */
close_console(shtctl->sheets0 + (i - 768));
} else if (1024 <= i && i <= 2023) {
close_constask(taskctl->tasks0 + (i - 1024));
}
但是运行之后就发现了BUG,因为ncst命令运行之后打开了一个新的命令行任务,但是应用窗口运行完之后这个任务就关闭了。所以点击应用程序窗口的关闭按钮没有任何反应。因为命行任务已经关闭了。估计下一节就是处理这个问题。