You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 43 | 输入输出设备我们并不是只能用灯泡显示“0”和“1”
我们在前面的章节搭建了最简单的电路在这里面计算机的输入设备就是一个一个开关输出设备呢是一个一个灯泡。的确早期发展的时候计算机的核心是做“计算”。我们从“计算机”这个名字上也能看出这一点。不管是中文名字“计算机”还是英文名字“Computer”核心都是在”计算“这两个字上。不过到了今天这些“计算”的工作更多的是一个幕后工作。
我们无论是使用自己的PC还是智能手机大部分时间都是在和计算机进行各种“交互操作”。换句话说就是在和输入输出设备打交道。这些输入输出设备也不再是一个一个开关或者一个一个灯泡。你在键盘上直接敲击的都是字符而不是“0”和“1”你在显示器上看到的也是直接的图形或者文字的画面而不是一个一个闪亮或者关闭的灯泡。想要了解这其中的关窍那就请你和我一起来看一看计算机里面的输入输出设备。
## 接口和设备:经典的适配器模式
我们在前面讲解计算机的五大组成部分的时候,我看到这样几个留言。
一个同学问像蓝牙、WiFi无线网卡这样的设备也是输入输出设备吗还有一个同学问我们的输入输出设备的寄存器在哪里到底是在主板上还是在硬件设备上
这两个问题问得很好。其实你只要理解了这两个问题,也就理解输入输出设备是怎么回事儿了。
实际上,输入输出设备,并不只是一个设备。大部分的输入输出设备,都有两个组成部分。第一个是它的**接口**Interface第二个才是**实际的I/O设备**Actual I/O Device。我们的硬件设备并不是直接接入到总线上和CPU通信的而是通过接口用接口连接到总线上再通过总线和CPU通信。
![](https://static001.geekbang.org/resource/image/ce/bf/ce9d22a0dafa20b9574411b810c0aabf.jpg)
[图片来源](https://www.flickr.com/photos/30478819@N08/42510913460/in/photolist-e3Uor9-a8hPhk-9geU8f-27LxxvN-7ihCSj-8pMS6t-eHwMd-eHwKr-gbeKdb-eHwLn-eHwMZ-gbexZR-aBK9Bc-5RXawg-aBK9uc-aBK9y4-5A1rvL-225eB2A-eHwNH-7TLGVg-4eNMHD-4eNN98-ckC5vN-4eNNBK-25n38Tv-67FwAd-GJ7fs1-225Mqxb-GJ7fM9-2f71bD4-2e17jxW-2gwg8wm-2gwg8a4-2gwfLgK-RwCq8x-f6REoz-3qw8YH)
SATA硬盘上面的整个绿色电路板和黄色的齿状部分就是接口电路黄色齿状的就是和主板对接的接口绿色的电路板就是控制电路
你平时听说的并行接口Parallel Interface、串行接口Serial Interface、USB接口都是计算机主板上内置的各个接口。我们的实际硬件设备比如使用并口的打印机、使用串口的老式鼠标或者使用USB接口的U盘都要插入到这些接口上才能和CPU工作以及通信的。
接口本身就是一块电路板。CPU其实不是和实际的硬件设备打交道而是和这个接口电路板打交道。我们平时说的设备里面有三类寄存器其实都在这个设备的接口电路上而不在实际的设备上。
那这三类寄存器是哪三类寄存器呢它们分别是状态寄存器Status Register、 命令寄存器Command Register以及数据寄存器Data Register
除了内置在主板上的接口之外,有些接口可以集成在设备上。你可能都没有见过老一点儿的硬盘,我来简单给你介绍一下。
上世纪90年代的时候大家用的硬盘都叫作**IDE硬盘**。这个IDE不是像IntelliJ或者WebStorm这样的软件开发集成环境Integrated Development Environment的IDE而是代表着集成设备电路Integrated Device Electronics。也就是说设备的接口电路直接在设备上而不在主板上。我们需要通过一个线缆把集成了接口的设备连接到主板上去。
![](https://static001.geekbang.org/resource/image/30/47/30c96ac2fd8a0deffcff86e7b66acf47.png)
我自己使用的PC的设备管理器
把接口和实际设备分离,这个做法实际上来自于计算机走向[开放架构](https://en.wikipedia.org/wiki/Open_architecture)Open Architecture的时代。
当我们要对计算机升级我们不会扔掉旧的计算机直接买一台全新的计算机而是可以单独升级硬盘这样的设备。我们把老硬盘从接口上拿下来换一个新的上去就好了。各种输入输出设备的制造商也可以根据接口的控制协议来设计和制造硬盘、鼠标、键盘、打印机乃至其他种种外设。正是这样的分工协作带来了PC时代的繁荣。
其实在软件的设计模式里也有这样的思路。面向对象里的面向接口编程的接口就是Interface。如果你做iOS的开发Objective-C里面的Protocol其实也是这个意思。而Adaptor设计模式更是一个常见的、用来解决不同外部应用和系统“适配”问题的方案。可以看到计算机的软件和硬件在逻辑抽象上其实是相通的。
如果你用的是Windows操作系统你可以打开设备管理器里面有各种各种的Devices设备、Controllers控制器、Adaptors适配器。这些其实都是对于输入输出设备不同角度的描述。被叫作Devices看重的是实际的I/O设备本身。被叫作Controllers看重的是输入输出设备接口里面的控制电路。而被叫作Adaptors则是看重接口作为一个适配器后面可以插上不同的实际设备。
## CPU是如何控制I/O设备的
无论是内置在主板上的接口还是集成在设备上的接口除了三类寄存器之外还有对应的控制电路。正是通过这个控制电路CPU才能通过向这个接口电路板传输信号来控制实际的硬件。
我们先来看一看,硬件设备上的这些寄存器有什么用。这里,我拿我们平时用的打印机作为例子。
![](https://static001.geekbang.org/resource/image/fd/38/fd788de17028e8b1dbce58de5da31e38.jpeg)
1. 首先是数据寄存器Data Register。CPU向I/O设备写入需要传输的数据比如要打印的内容是“GeekTime”我们就要先发送一个“G”给到对应的I/O设备。
2. 然后是命令寄存器Command Register。CPU发送一个命令告诉打印机要进行打印工作。这个时候打印机里面的控制电路会做两个动作。第一个是去设置我们的状态寄存器里面的状态把状态设置成not-ready。第二个就是实际操作打印机进行打印。
3. 而状态寄存器Status Register就是告诉了我们的CPU现在设备已经在工作了所以这个时候CPU你再发送数据或者命令过来都是没有用的。直到前面的动作已经完成状态寄存器重新变成了ready状态我们的CPU才能发送下一个字符和命令。
当然在实际情况中打印机里通常不只有数据寄存器还会有数据缓冲区。我们的CPU也不是真的一个字符一个字符这样交给打印机去打印的而是一次性把整个文档传输到打印机的内存或者数据缓冲区里面一起打印的。不过通过上面这个例子相信你对CPU是怎么操作I/O设备的应该有所了解了。
## 信号和地址:发挥总线的价值
搞清楚了实际的I/O设备和接口之间的关系一个新的问题就来了。那就是我们的CPU到底要往总线上发送一个什么样的命令才能和I/O接口上的设备通信呢
CPU和I/O设备的通信一样是通过CPU支持的机器指令来执行的。
如果你回头去看一看[第5讲](https://time.geekbang.org/column/article/93359)MIPS的机器指令的分类你会发现我们并没有一种专门的和I/O设备通信的指令类型。那么MIPS的CPU到底是通过什么样的指令来和I/O设备来通信呢
答案就是和访问我们的主内存一样使用“内存地址”。为了让已经足够复杂的CPU尽可能简单计算机会把I/O设备的各个寄存器以及I/O设备内部的内存地址都映射到主内存地址空间里来。主内存的地址空间里会给不同的I/O设备预留一段一段的内存地址。CPU想要和这些I/O设备通信的时候呢就往这些地址发送数据。这些地址信息就是通过上一讲的地址线来发送的而对应的数据信息呢自然就是通过数据线来发送的了。
而我们的I/O设备呢就会监控地址线并且在CPU往自己地址发送数据的时候把对应的数据线里面传输过来的数据接入到对应的设备里面的寄存器和内存里面来。CPU无论是向I/O设备发送命令、查询状态还是传输数据都可以通过这样的方式。这种方式呢叫作**内存映射**IOMemory-Mapped I/O简称MMIO
![](https://static001.geekbang.org/resource/image/bb/22/bb8c1c007f7263bee41b7c649304c722.jpeg)
那么MMIO是不是唯一一种CPU和设备通信的方式呢答案是否定的。精简指令集MIPS的CPU特别简单所以这里只有MMIO。而我们有2000多个指令的Intel X86架构的计算机自然可以设计专门的和I/O设备通信的指令也就是 in 和 out 指令。
Intel CPU虽然也支持MMIO不过它还可以通过特定的指令来支持端口映射I/OPort-Mapped I/O简称PMIO或者也可以叫独立输入输出Isolated I/O
其实PMIO的通信方式和MMIO差不多核心的区别在于PMIO里面访问的设备地址不再是在内存地址空间里面而是一个专门的端口Port。这个端口并不是指一个硬件上的插口而是和CPU通信的一个抽象概念。
无论是PMIO还是MMIOCPU都会传送一条二进制的数据给到I/O设备的对应地址。设备自己本身的接口电路再去解码这个数据。解码之后的数据呢就会变成设备支持的一条指令再去通过控制电路去操作实际的硬件设备。对于CPU来说它并不需要关心设备本身能够支持哪些操作。它要做的只是在总线上传输一条条数据就好了。
这个其实也有点像我们在设计模式里面的Command模式。我们在总线上传输的是一个个数据对象然后各个接受这些对象的设备再去根据对象内容进行实际的解码和命令执行。
![](https://static001.geekbang.org/resource/image/4e/a7/4e66bafd713fed95a4957df71b3bd8a7.png)
这是我计算机上,设备管理器里显卡设备的资源信息
这是一张我自己的显卡在设备管理器里面的资源Resource信息。你可以看到里面既有Memory Range这个就是设备对应映射到的内存地址也就是我们上面所说的MMIO的访问方式。同样的里面还有I/O Range这个就是我们上面所说的PMIO也就是通过端口来访问I/O设备的地址。最后里面还有一个IRQ也就是会来自于这个设备的中断信号了。
## 总结延伸
好了讲到这里不知道现在你是不是可以把CPU的指令、总线和I/O设备之间的关系彻底串联起来了呢我来带你回顾一下。
CPU并不是发送一个特定的操作指令来操作不同的I/O设备。因为如果是那样的话随着新的I/O设备的发明我们就要去扩展CPU的指令集了。
在计算机系统里面CPU和I/O设备之间的通信是这么来解决的。
首先在I/O设备这一侧我们把I/O设备拆分成能和CPU通信的接口电路以及实际的I/O设备本身。接口电路里面有对应的状态寄存器、命令寄存器、数据寄存器、数据缓冲区和设备内存等等。接口电路通过总线和CPU通信接收来自CPU的指令和数据。而接口电路中的控制电路再解码接收到的指令实际去操作对应的硬件设备。
而在CPU这一侧对CPU来说它看到的并不是一个个特定的设备而是一个个内存地址或者端口地址。CPU只是向这些地址传输数据或者读取数据。所需要的指令和操作内存地址的指令其实没有什么本质差别。通过软件层面对于传输的命令数据的定义而不是提供特殊的新的指令来实际操作对应的I/O硬件。
## 推荐阅读
想要进一步了解CPU和I/O设备交互的技术细节我推荐你去看一看北京大学在Coursera上的视频课程《计算机组成》[第10周的](https://www.coursera.org/learn/jisuanji-zucheng/home/week/10)[内容](https://www.coursera.org/learn/jisuanji-zucheng/home/week/10)。这个课程在Coursera上是中文的而且可以免费观看。相信这一个小时的视频课程对于你深入理解输入输出设备会很有帮助。
## 课后思考
我们还是回到这节开始的时候同学留言的问题。如果你买的是一个带无线接收器的蓝牙鼠标你需要把蓝牙接收器插在电脑的USB接口上然后你的鼠标会和这个蓝牙接收器进行通信。那么你能想一下我们的CPU和蓝牙鼠标这个输入设备之间的通信是怎样的吗
你可以好好思考一下,然后在留言区写下你的想法。当然,你也可以把这个问题分享给你的朋友,拉上他一起学习。