- 在讲完了 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 从缓冲区中读取数据并输出