1、设备管理框架
对于不同类型的设备的操作,全部由一下函数指针来完成。即操作系统对设备进行操作,只需要调用统一的API接口,无需了解相关的细节。
比如如下的接口设计:
int (*open) (device_t * dev) ;
int (*read) (device_t * dev, int addr, char * buf, int size);
int (*write) (device_t * dev, int addr, char * buf, int size);
int (*control) (device_t * dev, int cmd, int arg0, int arg1);
void (*close) (device_t * dev);
2、将tty纳入设备管理器中
介绍了如何将键盘和鼠标同时进行管理,从而为后续创建命令行解释器打下基础
此外,如果你参考相关资源,可能会看到tty设备对数据的处理有两种模式:
-
raw模式:对输入输出的数据不做任何处理
-
cooked模式:对数据做一些处理,然后再输入输出
在本门课程中,只采用了cooked模式,并且只做了非常简单的处理,即处理\r\n字符的转换。这个将在后面的课程中看到
3、为tty设备添加结构描述
希望最终做出的系统能够支持同时打开多个命令行,并且根据显存的特性,得知32KB的显存可能被用于显示8屏的数据。在代码中定义了tty_devs数组,用于表示多个tty设备
每个tty设备,使用32KB显存中的4KB来存储显示数据;输入全部采用同一块物理键盘。在后面课时中,这个键盘的数据是如何分发给每个tty设备。
在tty设备中,还增加了输入输出的缓存。缓存的主要作用是缓存处理器和外部设备工作速度的不一致
4、打开tty设备并向其写入数据
目的是在进程中能够调用open系统调用打开tty设备,然后通过printf函数向tty设备文件写入数据。
5、文件系统简介及初始化
文件系统的常见功能是将磁盘上的数据抽像成文件和目录,并将其组织成目录树的形式,一切皆文件(设备,外设接口)
6、为进程添加文件打开表
每个应用程序在运行过程中,都有可能打开文件并进行读写,且各个应用程序之间操作文件行为应当相互不干扰,以及多个进程同时打开同一文件中要处理冲突的问题
解决办法:
定义一个全局的file_t文件表,其中包含所有打开的文件表。每个进程只维护一个指针,指向表中的某个文件。每个进程既有自己的打开文件表,同时也能够方便实现文件地共享、处理打开同一文件的冲突问题。
比如,当打开某一文件时,可以先遍历file_t文件表,看看相应的文件是否已经打开。如果已经打开,则可以直接将进程自己的打开文件表中的某表项直接指向该文件即可。
6、从tty读取键值字符串并显示
tty设备的输入来源于键盘中断,当中断发生时产生按键的键值。
该键值会被保存在tty的输入缓存中,并且通过信号量来告诉进程缓存中有键值数据
- 按键的数据可能暂时被缓存而不必要求当时必须有进程在读取数据
- 当缓存中没有数据时,进程也可以在信号量上等待,从而释放CPU给其它进程
主要工作流程:
- 进程检查缓存是否有数据,如果有使用tty_fifo_get取出。如果没有则在信号量上等待
- 按键中断读取键值,写入tty_fifo_put,发送信号量通知进程去取数据。如果此刻有进程在等待,则唤醒进程。
6、打开标准输出和错误输出文件
目的实现dup系统调用,从而让应用程序能够通过stdin、stdout和stderr进行输入输出
printf实际是往其内部的stdout文件写数据
scanf则是从其内部的stdin文件读取文件
stderr文件,用于输出错误信息
这三个文件,在Newlib中,其对应的序号分别为stdin - 0, stdout - 1, stderr - 2。在我们目前的设计中,这三个文件实际上都对于同一个tty设备,即从显示器输出,从键盘输入。
我们可以同时打开同一文件3次。由于目前没有处理同一文件被打开多次的情况,因此实现了dup函数,其功能是实现一个已打开的文件的描述符副本。
7、允许切换tty窗口
显示器某一时刻只能将显存中的一部分用于显示,具体可以通过相应的显存寄存器来控制显示器从显存中的哪个位置开始取数据显示。
因此,采用创建多个tty,每个tty对应于显存中的一屏显示数据。最终可以创建多个终端设备,只不过这些设备都是虚拟的。限于屏幕和键盘均只有一个,因此每一时刻仅能使用某中某个tty设备。
要使用某个特定的tty设备,需要使用CTRL + Fn(n=1,2,....)来切换
8、为每个进程创建进程保护
考虑到tty设备的读写代码会被多进程访问,以及被中断访问,因此加上相应的保护