---- 整理自狄泰软件唐佐林老师课程
查看所有文章链接:(更新中)深入浅出操作系统 - 目录
1. 问题
- 驱动键盘之后能做什么?
2. Shell任务简介
- Shell的 本质是一个 任务(执行于用户态)
- 简单的命令行用户界面
- 提供用户与系统交互的接口
- 命令解释器,解释用户输入的命令
- 根据用户命令启动相应的系统功能
2.1 实现Shell任务的关键
- 获取键盘输入的字符
2.2 思考
- 当Shell请求用户输入时,用户是否正在输入字符?
不一定正在输入字符 ==> shell任务等待 ==> 可执行态切换为等待状态 - 当多个任务请求用户输入时,哪个任务应该获得输入的字符?
2.3 解决方案设计
- 定义内核中 事件 的概念
- 当任务请求输入时(调用ReadKey()时)产生事件
- 事件产生:任务进入等待状态
- 事件销毁:任务进入可执行状态
- Task模块根据 事件描述 执行具体的调度动作
- 如:操作哪一个等待队列
2.4 事件机制的设计
-
事件定义:触发任务在 可执行态 和 等待状态 之间切换的因素
-
type:描述事件类型(为什么发生状态切换?)
- 比如:
- 命名KeyEvent,获取用户输入,用户并没有真的输入时进入等待的场景
- 命名MutexEvent,由于获取互斥锁不成功而进入等待状态的场景
- 命名TaskEvent,触发任务进入等待状态的事件
- 比如:
-
id:事件标识信息(调度的关键)
-
param1:事件参数(可选)
-
param2:事件参数(可选)
- 事件 - 来统一任务需要等待的情况
- 事件产生:进入等待
- 再次进入可执行态:事件销毁
3. 模块重构
- Task模块
EventSchedule(action, event):根据事件执行调度
WaitTask(name):通过事件让任务进入等待状态 - Mutex模块
为每个互斥锁增加一个等待队列
无法获取锁的任务进入该锁的等待队列
3.1 Task模块重构
- 不同事件不同处理
3.2 Mutex模块重构
3.3 编程实验:模块重构
【参看链接】:75-76-77 - Shell任务的实现 / 75
3.4 思考
- Shell任务如何实现?系统调用uint ReadKey()如何实现?
4. Shell实现流程
用户界面 - 交互 - 输入命令
4.1 关键问题
- 当Shell请求用户输入时,用户是否正在输入字符?
- 大概率此时用户并没有正在输入 ==> 等待
- 当多个任务请求用户输入时,哪个任务应该获得输入的字符?
- 每个任务都应该获得输入
4.2 解决方案
4.3 解决方案实现
4.4 编程实验:Shell任务的实现(主要是输入部分)
【参看链接】:75-76-77 - Shell任务的实现 / 76
4.5 思考
- 如何实现Shell任务的用户接口功能? – 交互
5. Shell任务界面设计
5.1 Shell任务的完善
- 支持 删除键(Backspace)和 回车键(Enter)
- Backspace:修改已输入命令中的字符
- Enter:确认命令输入完毕
- 命令类型
- 启动型命令 - 启动任务
- 应用级命令 - 由shell实现,如clean
- 内核级命令 - shell依赖系统调用
5.2 缓存用户输入
- 思考:用户输入存储在哪?
- 定义全局字符缓冲区 gKBuf
- 存储用户输入的字符
- 定义全局变量 gKIndex
- 记录新字符在缓冲区中的位置
5.3 删除键(Backspace)的支持
- 从后向前删除命令行字符
- 删除字符后光标回退到上一个位置
- 获取PROMPT(即下图中的“D.T.OS >> ”打印)的长度
- 获取当前光标横坐标
- 当光标横坐标大于PROMPT长度时:
- 将光标前一个位置输出空格
- 将光标移动到前一个位置
- 当光标横坐标大于PROMPT长度时:
5.4 回车键(Enter)的支持
- 在字符缓冲区gKBuf中添加 0结束符
- 执行命令对应的功能
5.5 编程实验:Backspace & Enter
【参看链接】:75-76-77 - Shell任务的实现 / 77 / 00BackspaceAndEnter
5.6 命令映射设计
命令字符串 到 功能(入口函数) 之间的映射
- 将命令和命令入口进行映射(命令注册)
- 用户输入命令后,查找命令入口并执行
- 无法找到命令入口时,执行无效命令入口
5.6.1 命令注册
5.6.2 未知命令入口函数
5.7 编程实验:Shell任务的完善
【参看链接】:75-76-77 - Shell任务的实现 / 77 / 01命令映射
5.8 思考
- 内核级命令如何实现?