一个操作系统的设计与实现——第17章 系统交互
操作系统最终是供用户使用的,所以其需要具备与用户交互的能力,交互方式可以是命令行,图形界面,甚至是触摸屏,语音,实体按钮等。本章将要实现的是系统交互。
17.1 外壳程序
我们的操作系统使用的是基于命令行的交互模式。实现此功能的模块被称为外壳(Shell)程序。
事实上,上一章的Test.c
已经是一个简单的外壳程序了,它唯一的功能是:读取命令行输入,然后将其打印。因此,如果对命令行输入进行解析,根据其内容调用各种操作系统内核函数,就能得到一个具有实用价值的外壳程序。
我们的外壳程序支持以下四种命令:
l
命令;调用fsList
函数c 文件名 起始扇区号 扇区数
命令;调用fsCreate
函数d 文件名
命令。调用fsDelete
函数r 文件名
命令。调用fsLoad
函数
如果命令行输入不是上述命令中的一种,则打印输入的字符串。
17.2 外壳程序等待
我们的外壳程序支持的四个命令中,前三个都很容易实现,但第四个是有问题的。具体来说,如果直接调用fsLoad
函数,新任务就会和外壳程序同时处于任务队列中。这并非一个错误,后台运行模式正是基于此原理实现的。但我们的操作系统并不支持后台运行模式,当外壳程序加载一个任务后,其需要等待这个任务退出后才能继续执行。
想要让一个任务等待,就需要一个专用的队列。这项技术在实现IO队列时已经使用过了。具体来说,想要让外壳程序等待被加载任务的结束,就需要实现以下功能:
- 创建一个新的任务队列供外壳程序使用。不妨称之为外壳队列
- 当
fsLoad
函数调用后,立即发起任务切换。并将发起者,即外壳程序添加到外壳队列中 - 当任务退出时,从外壳队列中取出外壳程序,并将其重新添加到任务队列
17.3 系统交互的实现
17.3.1 外壳程序等待的实现
请看本章代码17/Task.h
。
第21行,声明了外部链接的外壳队列shellQueue
。
接下来,请看本章代码17/Task.hpp
。
第14行,定义了外壳程序专用的外壳队列shellQueue
。
第54行,在taskInit
函数中添加对外壳队列的初始化。
第217~219行,当3特权级任务加载完成后,将当前任务,即外壳程序的TCB中的taskQueue
数据成员设定为外壳队列,然后立即发起任务切换。此时,新任务已经位于任务队列中,而外壳程序将在外壳队列上等待。
接下来,请看本章代码17/Int.s
。
第12行,声明了外部链接的外壳队列shellQueue
。
第194行,将push taskQueue
修改为push shellQueue
。任务退出时,应从外壳队列中将正在等待的外壳程序取出,并重新添加到任务队列。这行代码与17/Task.hpp
中的第217~219行对应。
第198行,将外壳程序的TCB中的taskQueue
数据成员重新恢复为任务队列。
17.3.2 外壳程序的实现
请看本章代码17/Util.h
。
第21~22行,声明了本章新增的两个函数。
接下来,请看本章代码17/Util.hpp
。
isalnum
,isdigit
,nextStr
,nextNum
是本章新增的四个辅助函数。isalnum
和isdigit
函数与C语言标准库的同名函数等价;nextStr
函数用于取得给定字符串的下一个单词,其相当于C语言标准库的strtok
函数的简化版;nextNum
函数用于取得给定字符串的下一个整数,其相当于nextStr
函数与STL的stoi
函数的结合。
接下来,请看本章代码17/Shell.h
。
第3行,声明了shellInit
函数。
接下来,请看本章代码17/Shell.hpp
。
__parseCmd
函数用于解析命令行输入。
第12~15行,解析l
命令,并调用fsList
函数。
第16~25行,解析c 文件名 起始扇区号 扇区数
命令,并调用fsCreate
函数。
第26~31行,解析d 文件名
命令,并调用fsDelete
函数。
第32~37行,解析r 文件名
命令,并调用fsLoad
函数。
第38~41行,如果命令行输入不属于上述情况,则打印输入的字符串。
shell
函数实现的是外壳程序。
第47行,定义输入的字符串。
第49~54行,在循环中不断进行以下三个操作:
- 打印命令提示符
- 读取命令行输入
- 调用
__parseCmd
函数,以解析输入的字符串
shellInit
函数用于加载外壳程序。外壳程序调用了文件系统以及Util.hpp
中的函数,这些函数都是仅限0特权级使用的,所以外壳程序应加载为一个0特权级任务。读者也可以将外壳程序加载为一个3特权级任务,这就需要将其使用的所有函数都安装成系统调用,或在3特权级下以函数库的形式实现。
接下来,请看本章代码17/Kernel.c
。
第21行,调用shellInit
函数,完成外壳程序的初始化。
17.4 测试
本章使用的测试任务为17/Test.c
。其先读取字符串,再打印读取到的字符串。
启动bochs后,可以发现命令行已经开始工作。
在命令行中输入c test 1000 20
命令,再输入l
命令,可以发现test
文件已经安装完毕。
在命令行中输入r test
命令,可以发现test
程序已经开始运行,命令行正在等待用户输入,而此时的外壳程序处于等待状态。输入任意字符串后回车,可以发现test
程序在打印输入字符串后结束,外壳程序重新开始运行。
在命令行中输入d test
命令,再输入l
命令,可以发现test
文件已被删除。
我们的操作系统到这里就全部实现完成了。