不同的人对 I/O 硬件的理解也不同。对于电子工程师而言,I/O 硬件就是芯片、导线、电源和其他组成硬件的物理设备。而我们程序员眼中的 I/O 其实就是硬件提供给软件的接口
,比如硬件接受到的命令、执行的操作以及反馈的错误。我们着重探讨的是如何对硬件进行编程,而不是其工作原理。
I/O 设备
什么是 I/O 设备?I/O 设备又叫做输入/输出设备,它是人类用来和计算机进行通信的外部硬件。输入/输出设备能够向计算机发送数据(输出)
并从计算机接收数据(输入)
。
I/O 设备(I/O devices)
可以分成两种:块设备(block devices)
和 字符设备(character devices)
。
块设备
块设备是一个能存储固定大小块
信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写入数据。每个块都有自己的物理地址
。通常块的大小在 512 - 65536 之间。所有传输的信息都会以连续
的块为单位。块设备的基本特征是每个块都较为对立,能够独立的进行读写。常见的块设备有 硬盘、蓝光光盘、USB 盘
与字符设备相比,块设备通常需要较少的引脚。
块设备的缺点
基于给定固态存储器的块设备比基于相同类型的存储器的字节寻址要慢一些,因为必须在块的开头开始读取或写入。所以,要读取该块的任何部分,必须寻找到该块的开始,读取整个块,如果不使用该块,则将其丢弃。要写入块的一部分,必须寻找到块的开始,将整个块读入内存,修改数据,再次寻找到块的开头处,然后将整个块写回设备。
字符设备
另一类 I/O 设备是字符设备
。字符设备以字符
为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操作。常见的字符设备有 打印机、网络设备、鼠标、以及大多数与磁盘不同的设备。
下面显示了一些常见设备的数据速率。
设备控制器
首先需要先了解一下设备控制器的概念。
设备控制器是处理 CPU 传入和传出信号的系统。设备通过插头和插座连接到计算机,并且插座连接到设备控制器。设备控制器从连接的设备处接收数据,并将其存储在控制器内部的一些特殊目的寄存器(special purpose registers)
也就是本地缓冲区中。
特殊用途寄存器,顾名思义是仅为一项任务而设计的寄存器。例如,cs,ds,gs 和其他段寄存器属于特殊目的寄存器,因为它们的存在是为了保存段号。eax,ecx 等是一般用途的寄存器,因为你可以无限制地使用它们。例如,你不能移动 ds,但是可以移动 eax,ebx。
通用目的寄存器比如有:eax、ecx、edx、ebx、esi、edi、ebp、esp
特殊目的寄存器比如有:cs、ds、ss、es、fs、gs、eip、flag
”
每个设备控制器都会有一个应用程序与之对应,设备控制器通过应用程序的接口通过中断与操作系统进行通信。设备控制器是硬件,而设备驱动程序是软件。
I/O 设备通常由机械组件(mechanical component)
和电子组件(electronic component)
构成。电子组件被称为 设备控制器(device controller)
或者 适配器(adapter)
。在个人计算机上,它通常采用可插入(PCIe)扩展插槽
的主板上的芯片或印刷电路卡的形式。
机械设备就是它自己,它的组成如下
控制器卡上通常会有一个连接器,通向设备本身的电缆可以插入到这个连接器中,很多控制器可以操作 2 个、4 个设置 8 个相同的设备。
控制器与设备之间的接口通常是一个低层次的接口。例如,磁盘可能被格式化为 2,000,000 个扇区,每个扇区 512 字节。然而,实际从驱动出来的却是一个串行的比特流,从一个前导符(preamble)
开始,然后是一个扇区中的 4096 位,最后是一个校验和
或 ECC(错误码,Error-Correcting Code)
。前导符是在对磁盘进行格式化的时候写上去的,它包括柱面数和扇区号,扇区大小以及类似的数据,此外还包含同步信息。
控制器的任务是把串行的位流转换为字节块,并进行必要的错误校正工作。字节块通常会在控制器内部的一个缓冲区按位进行组装,然后再对校验和进行校验并证明字节块没有错误后,再将它复制到内存中。
内存映射 I/O
每个控制器都会有几个寄存器用来和 CPU 进行通信。通过写入这些寄存器,操作系统可以命令设备发送数据,接收数据、开启或者关闭设备等。通过从这些寄存器中读取信息,操作系统能够知道设备的状态,是否准备接受一个新命令等。
为了控制寄存器
,许多设备都会有数据缓冲区(data buffer)
,来供系统进行读写。例如,在屏幕上显示一个像素的常规方法是使用一个视频 RAM,这一 RAM 基本上只是一个数据缓冲区,用来供程序和操作系统写入数据。
那么问题来了,CPU 如何与设备寄存器和设备数据缓冲区进行通信呢?存在两个可选的方式。第一种方法是,每个控制寄存器都被分配一个 I/O 端口(I/O port)
号,这是一个 8 位或 16 位的整数。所有 I/O 端口的集合形成了受保护的 I/O 端口空间,以便普通用户程序无法访问它(只有操作系统可以访问)。使用特殊的 I/O 指令像是
IN REG,PORT
CPU 可以读取控制寄存器 PORT 的内容并将结果放在 CPU 寄存器 REG 中。类似的,使用
OUT PORT,REG
CPU 可以将 REG 的内容写到控制寄存器中。大多数早期计算机,包括几乎所有大型主机,如 IBM 360 及其所有后续机型,都是以这种方式工作的。
控制寄存器是一个处理器寄存器而改变或控制的一般行为 CPU 或其他数字设备。控制寄存器执行的常见任务包括中断控制,切换寻址模式,分页控制和协处理器控制。
”
在这一方案中,内存地址空间和 I/O 地址空间是不相同的,如下图所示
指令
IN R0,4
和
MOV R0,4
这一设计中完全不同。前者读取 I/O端口 4 的内容并将其放入 R0,而后者读取存储器字 4 的内容并将其放入 R0。这些示例中的 4 代表不同且不相关的地址空间。