操作系统-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函数中。