操作系统-Lab3
准备
实验以《orange's:一个操作系统的实现》的代码为基础
运行
问题1
make image失败

解决方法

结果

问题2
make image结束之后,执行下面的命令失败
1 | |
报错信息
1 | |
解决方法
1 | |
结果显示

已实现和待实现
已实现
- 从屏幕左上角开始,以白色显示键盘输入的字符,可以输入并显示a-z, A-Z和0-9字符。
- 支持大小写切换包括 Shift 组合键以及大写锁定两种方式,大写锁定后再用 Shift 组合键将会输入小写字母
- 支持回车键换行
- 支持用退格键删除输入内容
- 支持空格键
- 有光标显示
- 输入字符无上限
待实现
理解代码

首先,需要理解代码,可以照着《orange's:一个操作系统的实现》第七章的讲解一起看。
输出一个字符的函数调用逻辑

当初始化结束后,就会进入到task_tty()函数中,进行不断的循环。
探究CONSOLE结构体中四个变量的含义

分析过程

屏幕一行80个字符,屏幕总共25行。
在敲字符过程中,cursor在不断加1,original_addr始终为0x0,current_start_addr是当前屏幕可见范围的开始位置距离最开始显示位置的偏移。

按到最后

可以发现cursor的值最大只能到0x1554,因为被v_mem_limit所限制。
经过分析就可以基本确定CONSOLE结构体四个变量的含义,用一张图来表示一下

实现功能
TAB
在keyboard.h中可以发现TAB是一个不可显示的字符,所以在tty.c的in_process中要增加对于TAB的识别,将其放进缓冲区内。然后在console.c的out_char函数中增加对\t的输出处理。


清屏
清屏的功能实现在逻辑上比较简单,只要不断地调用out_char()函数,传入\b,直到cursor回到original_addr位置。
但是如何在实现每20秒清屏一次呢?要理解kernel_main()函数中的任务,可以新增一个任务,task_clear_screen,然后在其中执行清屏任务,并且每次执行完毕后延迟20s。
1、将task_clear_screen声明成一个任务,不断执行
在global.c中做相应修改,并修改对应的宏

2、在task_clear_screen任务中完成清屏逻辑,并且延迟20s

每个任务中while循环不能break,否则执行会报错。
退格一次完成
源代码的删除功能只实现了一个一个删除,即使是对于\n和\t也是如此,不符合实际情况,需要做修改,实现一次删除\t和\n。
思路
要想实现一次退格,就需要知道前一个字符是什么,如果是普通字符那么让光标位置减一即可,如果是特殊的\n和\t就需要特殊处理,使光标回到按下\n和\t前的位置。
对于\t还好说,只要把光标减4即可;但是对于\n来说,就有点麻烦了。
一开始,想让光标一直往回移动直到遇到不是空格的字符。但是如果在输入\n之前刚输入了空格呢?这样显然就不对,而且实现起来还很复杂。
所以需要借助新的数据结构来存储已经显示的字符,并且对显示的字符做一个包装,做成一个结构体。

再构建一个存储当前屏幕中所有显示的字符的结构体

然后需要在适当的地方将这个数据结构插入
过程
1、初始化C_BUF结构体

2、在将字符放入TTY中的同时,也将封装起来的字符放进C_BUF中

3、在out_char函数中实现对应逻辑
在其他输出字符的位置需要加上对CHAR结构体中before_cursor和after_cursor的赋值,例如:

然后在处理退格键时就可以用如下一段代码解决

4、显示字符结束后,需要修改C_BUF中的buf_cur_idx

这么设计就可以把所有的字符统一起来处理,在退格时只要回到按下该键前的光标位置即可。
注意需要把p_cbuf作为全局变量声明,这样可以在所有文件中使用,而不用作为函数参数传来传去。
在global.c和global.h中做声明即可
查找功能
思路
明确几个状态(代码也是依据状态编写的)
1、正常状态。以黑底白字显示字符,一切正常
2、搜索状态。在正常状态按下esc后,以黑底红字显示字符,并记录这段时间敲下的字符。
3、匹配状态。搜索状态按下enter后,进行match,思路是从头遍历C_BUF结构体数组,找到匹配的字符串后把相应的字符颜色改成黑底红字,再从头显示。
4、从匹配态退出。删去搜索字符串,把黑底红字的改成黑底白字,重新显示。
过程
1、定义一些全局变量和常量,对CHAR结构体进行调整(增加color字段)



状态的变化
1 | |
2、在每个状态编写相应逻辑
INIT
在in_process函数中增加对ESC的识别

这个初始化要小心,每次进入搜索状态都要进行初始化

SEARCH
改变in_process中对Enter键的处理


MATCH
在in_process函数开头加上判断,只接收ESC


在out_char函数中也要做相应处理
1、MATCH状态下是不需要向C_BUF数组中增添CHAR的,只做输出。
2、SEARCH状态下要记录搜索字符串,并且注意SEARCH状态下是黑底红色。

注意:在退格搜索字符串时(
SEARCH状态),不能将原来的字符串删除。
control+z撤销
这个也很简单,首先要判断出是否是按下ctrl+z组合键,然后执行退格操作即可。
似乎没有那么简单,因为既要撤销显示出来的字符,还要撤销删除,以及需要能够一直撤销直到初始状态。
想法1:再建立一个ACTION的列表,记录所有的操作。但是实现起来可能比较复杂,因为需要同步好几个数据结构数组之间的关系。
想法2:改造C_BUF数据结构。其实撤销的主要难点在于撤销退格,所以在C_BUF中使用两个指针。也不行,可操作性不高,指针移动比较复杂。
最终采用想法1,需要理清ACTION数组在什么位置更新!
1、创建数据结构
其中MAX_ACTION为1000,即支持大约1000次撤销。

2、初始化ACTION列表
在task_tty函数中调用初始化函数即可

3、向列表中增加ACTION
经过思考,选在tty_do_write函数的out_char后面

4、撤销操作
在in_process函数中增加以下逻辑,就是在判断出是Ctrl+z组合键后,回退ACTION数组,做逆操作。

注意虚拟机中的热键 VirtualBox默认是 Right Ctrl
导致Right Ctrl按不出来
总结
由于上面的描述是写完一个功能后立刻写下的,所以导致代码前后会出现不一致。
感觉十分重要的调整是 【将字符加进C_BUF结构体中buf 】的位置 和 【更新 C_BUF结构体中 buf_cur_idx】 的位置,最后是都放在了out_char函数中。