- 在讲完了 CPU 管理和内存管理后,接下来就是 I/O 设备的管理,和前面一样,I/O 设备的管理就是通过对 I/O 设备的使用引出来的。
L26 I/O与显示器
★I/O设备的使用
在讲完了 CPU 管理和内存管理后,接下来就是 I/O 设备的管理,和前面一样,I/O 设备的管理就是通过对 I/O 设备的使用引出来的。
对每个 I/O 设备,都有一个控制器(例如显示器的控制器是显卡),CPU 通过对这些控制器中的寄存器发送指令,就可以让这些控制器控制 I/O 设备去做指令所要求做的事,这时 CPU 就可以去做别的事情了;等到设备做完了指令所要求做的事后,就会向 CPU 发出中断,让 CPU 去做(在 I/O 设备做完指令所要求做的事后)剩下的事情,即中断处理。
所以 CPU 要做的就是两件事:一是用类似于
out xx.al的指令让 I/O 设备工作;二是处理 I/O 设备完成指令后发出的中断,进行中断处理。为了方便 CPU 向设备控制器的寄存器写指令,还需要操作系统给用户提供一个简单的视图——文件视图,来方便用户使用 I/O 设备
如下面两张图所示,文件视图的作用在于,不论是使用了什么设备,操作系统都为用户提供了统一的接口:
open、read、write和close,这就方便了用户使用 I/O 设备。★总结:I/O 设备的使用总结起来就是三件事:
- CPU 用类似于
out xx.al的指令让 I/O 设备工作; - 操作系统给用户提供一个简单的视图——文件视图,来方便用户使用 I/O 设备;
- CPU 处理 I/O 设备完成指令后发出的中断,进行中断处理。
- CPU 用类似于
显示器
接下来以显示器显示输出来具体描述 I/O 设备的使用过程(终端设备包括显示器和键盘)。
首先在用户程序里,通过调用
printf函数来输出内容,而由前面已知,printf是一个库函数,将该库函数展开,包含有文件接口write,所以一切的故事就从write这里展开…
文件视图
前面已经说过,不管对于什么设备,都用
open、read、write和close接口来使用设备,所以在write函数那里,就要进行“分支”,根据传进来的参数来确定到底是对什么设备(如键盘、显示器、磁盘等)进行 write 操作,在下图中,就往write接口中传进去了参数 “1”,根据传进来的参数 “1” 得到对应的 inode,该 inode 中存储了显示器的信息。那么
filp数组是怎么来的呢?因为对一个进程来说,它都是通过父进程fork来的,它的 PCB 的大部分内容都复制了父进程的 PCB,所以就要找到最初的那个进程——进程 0,在init函数中,发现有一句(void) open("/dev/tty0",O_RDWR,0);,打开了dev/tty0文件,而在该文件中,就存储了终端设备的内容。于是继续进入
(void) open("/dev/tty0",O_RDWR,0);语句,通过sys_open系统调用看到,inode 正是在这里。所以
(void) open("/dev/tty0",O_RDWR,0);的核心就是建立了这样一个链:PCB 中存储有filp数组,该数组中的每一项对应了file_table中的每一项 f ,然后file_table中的每一项 f 又存储有 inode,inode 中就有设备的信息。根据上面所说的内容,就实现了不管对于什么设备,都可以统一用四个系统接口实现对设备的使用。
字符设备接口
在得到了 inode 之后,就需要根据 inode 中的信息进一步确定是要对显示器进行写操作,如下图所示。
由于显示器是字符设备,所以又跳到
rw_char函数执行,rw_char函数有crw_table,是一个函数指针表(字符设备接口),根据该函数指针表及传进去的参数找到rw_ttyx,由于是写,所以传给形参 rw 的实参为 WRITE,所以调用tty_write将要输出的内容存储到缓冲中。
tty_write和con_write
最后真正开始往显示器写入要输出的内容,**
tty_write是将数据写到缓冲区里,而con_write则是从缓冲区中读取数据,真正输出到显示器上**。下面两张图是关于
pos的内容。★总结:文件视图(系统接口) -> 字符设备接口 -> tty_write-> 缓冲区 -> con_write 从缓冲区中读取数据并输出