操作系统学习笔记10 | I/O、显示器与键盘
作者:互联网
1. 外设工作原理的主干理解
与 内存管理 的理解过程相似,对于 IO设备(也叫外设)的理解,我们回到计算机的工作原理-- 冯·诺依曼的存储程序、取指执行思想。
IO设备分为两类:
- 键盘和显示器,本文先聚焦这部分;
- 磁盘,这部分下一篇会详解;
后续会在磁盘驱动的基础上抽象出文件,最后所以会讲文件系统。
计算机如何让外设工作的呢?
-
根据生活经验,每个外设都会有对应的控制设备,
比如显示器对应显卡;磁盘对应磁盘控制器...
这些设备内部是寄存器。
-
核心原理:向对应的 外设控制器 / 设备控制器 发指令,外设控制器根据其中的 寄存器(或是 memory)的值来操控对应硬件
如 显卡控制显存输出到显示器、或是在其内部的计算电路执行一些计算(GPU是高效的并行计算硬件);
具体表现为 out 指令:
out xx,al
外设部分 所有的代码落实到最后都是这个 out 指令。
-
CPU 向 外设控制器 发送指令(通过 PCI 总线)后,进入阻塞切换到其他进程。
-
外设工作完后会向CPU发送中断,CPU再接着执行相关的中断处理程序
核心代码思路就是上面 发指令、外设工作、中断处理这几步;但为了让不同用途、不同厂家、不同型号的外设使用起来简单,还需要虚拟化、抽象化为统一的视图:文件视图。所以又加上了很多代码来进行包装。
抽象为文件视图是一个很重要的概念,这让上层用户只要想输出到显示器上,都可以统一使用 printf 函数,而不需要考虑显示器是什么型号。
总结:
- 抽象化为统一的文件视图;
- CPU发外设控制指令;
- 外设工作后返回中断处理;
2. 显示器的工作理解
我们采用自顶向下的方式看看一段控制显示器输出的高级语言程序是如何被外设执行的。
2.1 文件视图
从学习笔记2-系统调用 得知(当时的配图如下图1),printf 远远不是 ”输出" 的真相,它的C语言函数库中是如下代码。下面的open、write、close 都是什么意思呢?
//打开显示器对应的文件,此时显示器已经被抽象为文件
int fd = open("/dev/xxx");
for(int i = 0; i < 10; i++){
write(fd,i,sizeof(int));
}
close(fd);
由于外设被抽象为文件,所以讲解 printf 的显示机制之前,还需要了解一下整个文件视图的全貌。
如上面代码所示,操作系统为用户操作各种外设提供了统一的文件操作接口:
- 操作函数:如上面代码中提到的 open、write、close,此外还有 read;
- 操作对象:即
"/dev/xxx"
,不同的设备名对应的就是不同的设备。根据这里的不同来区分设备,进而据此决定后续控制哪种硬件进行操作;
接下来,文件接口的操作函数,根据操作对象(C语言中的文件名)的不同,进行相应的控制硬件的处理。这就是下图的第二层:
如这里我们这里的代码是 printf 的展开,文件名对应的是显示器,对应就控制显示器。
在向下写控制器的指令就是 out 指令,向显示器控制器写入相应内容,控制器经过处理将指令作用到硬件上。这就是下图的最底一层。
当某些外设控制器处理完毕,就会向CPU返回中断,进行一些中断处理,再返回到 文件系统的接口层(write、close)这里。
比如键盘,按下键位后返回中断。
文件的读写的数据来源来自内存,如果是 printf,就是将数据从内存里的某段缓冲区取出字符打到显示器上,而如果是 fprintf,就是内存该区域的相关字符放到磁盘的相应块上。
2.2 从高级程序到文件接口
高级语言中,如果要输出一段字符,我们通常使用:
printf("Host Name:%s",name);
我们知道 printf 并非事情真相,它会继续展开为一段包含 write 的 函数库代码:write(1,buf,...);
意思是将 buf 这里的字符串 写到 1 这个地方。
至于这里的 1 是什么意思,见下面 2.3 sys_write。不过显而易见,write 再向下就会变成一段含有 out 指令的代码。
根据上面的文件系统,write 这个文件接口会根据操作对象不同进行分支,选择不同的第二层操作,比如操作显示器时就执行显示器分支。
2.3 内核层接口实现
上面进行到了文件接口,接口通过 int 0x80 指令中断进入内核,这在 <学习笔记2 | 操作系统接口> 详细讲过,这里就是继续向下完成内核中接口的实现,也就是内核层接口实现。