# I/O 设备的概念和分享
什么是 I/O 设备
I/O 设备就是可以将数据输入到计算机,或者可以接受计算机输出数据的外部设备,属于计算机中的硬件部件
UNIX 系统将外部设备抽象为一种特殊的文件,用户可以使用与文件操作相同的方式对外部设备进行操作
I/O 设备的分类 —— 按使用特性
- 人机交互类外部设备:
- 鼠标、键盘、打印机等
- 数据传输速度慢
- 存储设备
- 移动硬盘、光盘等
- 数据传输速度快
- 网络通信设备
- 路由器等
- 介于上述二者之间
I/O 设备的分类 —— 按传输速率分类
- 低速设备
- 鼠标、键盘等
- 传输速率每秒几个到几百字节
- 中速设备
- 激光打印机
- 传输速率每秒千至上万个字节
- 高速设备
- 磁盘等
- 传输速率为每秒数千至千兆字节的设备
I/O 设备的分类 —— 按信息交换的单位分类
- 块设备
- 磁盘等
- 数据传输单位为块,传输速率比较高,可寻址
- 字符设备
- 鼠标、键盘等
- 数据传输单位是字符,传输速率慢,不可寻址
# I/O 控制器
# I/O 设备的机械部件
I/O 设备的机械部件主要用来执行具体 I/O 操作
I/O 设备的电子部件通常是一块插入主板扩充槽的印刷电路板
# I/O 设备的电子部件(I/O 控制器)
CPU 无法直接控制 I/O 设备的机械部件,因此 I/O 设备还要有一个电子部件作为 CPU 和 I/O 设备机械部件之间的 "中介",用于实现 CPU 对设备的控制。
这个电子部件就是 I/O 控制器,又称设备控制器。CPU 可控制 I/O 控制器,又由 I/O 控制器来控制设备的机械部件
I/O 控制器的功能:
- 接受和识别 CPU 发出的命令
- 如 CPU 发来的 read/write 命令,I/O 控制器中会有相应的控制寄存器来存放命令和参数
- 向 CPU 报告设备的状态
- I/O 控制器中会有相应的状态寄存器,用于记录 I/O 设备的当前状态。如 1 表示空闲,0 表示忙碌
- 数据交换
- I/O 控制器中会设置相应的数据寄存器。输出时,数据寄存器用于暂存 CPU 发来的数据,之后再由控制器传送设备。输入时,数据寄存器用于暂存设备发来的数据,之后 CPU 从数据寄存器中取走数据
- 地址识别
- 类似于内存的地址,为了区分设备控制器中的各个寄存器,也需要给各个寄存器设置一个特定的 "地址"。I/O 控制器通过 CPU 提供的 "地址" 来判断 CPU 要读 / 写的是哪个寄存器
# I/O 控制器的组成
- I/O 逻辑
- 负责接收和识别 CPU 的各种命令(如地址译码),并负责对设备发出命令
- CPU 与控制器的接口
- 用于实现 CPU 与控制器之间的通信。CPU 通过控制线发出命令;通过地址线指明要操作的设备;通过数据线来取出(输入)数据,或放入(输出)数据
- 控制器与设备的接口
- 用于实现控制器与设备之间的通信
注意:
- 一个 I/O 控制器可能会对应多个设备
- 数据寄存器、控制寄存器、状态寄存器可能有多个(如:每个控制 / 状态寄存器对应一个具体的设备),且这些寄存器都要有相同的地址,才能方便 CPU 操作。有的计算机会让这些寄存器占用内存地址的一部分,称为内存映象 I/O,另一些计算机则采用 I/O 专用地址,即寄存器独立编址
内存映象 I/O vs 寄存器独立编址
内存映象 I/O:
- 内存映射 I/O。控制器中的寄存器与内存地址统一编址
- 优点:简化了指令。可以采用对内存进行操作的指令来对控制器进行操作
寄存器独立编制:
寄存器独立编制。控制器中的寄存器使用单独的地址
缺点:需要设置专门的指令来实现对控制器的操作,不仅要指明寄存器的地址,还要指明控制器的编号
# I/O 控制方式
# 程序直接控制方式
轮询
- 完成一次读 / 写操作的流程(以读操作为例)
- CPU 向控制器发出读指令。于是设备启动,并且状态寄存器设为 1(未就绪)
- 轮询检查控制器的状态(其实就是在不断地执行程序的循环,若状态位一直是 1,说明设备还没准备好要输入的数据,于是 CPU 会不断地轮询)
- 输入设备准备好数据后将数据传给控制器,并报告自身状态
- 控制器将输入的数据放到数据寄存器中,并将状态数改为 0(已就绪)
- CPU 发现设备已就绪,即可将数据寄存器中的内容读入 CPU 的寄存器中,再把 CPU 寄存器中的内容放入内存
- 若还要继续读入数据,则 CPU 继续发出读指令
- CPU 干预的频率
- 很频繁,I/O 操作开始之前、完成之后需要 CPU 介入,并且在等待 I/O 完成的过程中 CPU 需要不断地轮询检查
- 数据传送的单位:每次读 / 写一个字
- 数据的流向
- 读操作(数据读入):I/O 设备 ->CPU(指 CPU 的寄存器)-> 内存
- 写操作(数据输出):内存 ->CPU->I/O 设备
- 每个字的读 / 写都需要 CPU 的帮助
- 主要缺点和主要优点
- 优点:实现简单。在读 / 写指令之后,加上实现循环检查的一系列指令即可(因此才称为 "程序直接控制方式")
- 缺点:CPU 和 I/O 设备只能串行工作,CPU 需要一直轮询检查,长期处于 "忙等" 状态,CPU 利用率低
# 中断驱动方式
引入中断机制。由于 I/O 设备速度很慢,因此在 CPU 发出读 / 写命令后,可将等待 I/O 的进程阻塞,先切换到别的进程执行。当 I/O 完成后,控制器会向 CPU 发出一个中断信号,CPU 检测到中断信号后,会保存当前进程的运行环境信息,转去执行中断处理程序处理该中断。处理中断的过程中,CPU 从 I/O 控制器读一个字的数据传送到 CPU 寄存器,再写入主存。接着,CPU 恢复等待 I/O 的进程(或其他进程)的运行环境,然后继续执行
注意:
CPU 会在每个指令周期的末尾检查中断
中断处理过程中需要保存、恢复进程的运行环境,这个过程是需要一定时间开销的。可见,如果中断发生的频率太高,也会降低系统性能
CPU 的干预频率:每次 I/O 操作开始之前,完成之后需要 CPU 介入。等待 I/O 完成的过程中 CPU 可以切换到别的进程执行
数据传送的单位
- 每读 / 写一个字
数据的流向:
- 读操作(数据读入):I/O 设备 ->CPU(指 CPU 的寄存器)-> 内存
- 写操作(数据输出):内存 ->CPU->I/O 设备
主要优点和主要缺点:
- 优点:与 "程序控制方式" 相比,在 "中断驱动方式" 中,I/O 控制器会通过中断信号主动报告 I/O 已完成,CPU 不再需要不停地轮询。CPU 和 I/O 设备可并行工作,CPU 利用率得到明显提升
- 缺点:每个字在 I/O 设备与内存之间的传输,都需要经过 CPU。而频繁的中断处理会消耗较多的 CPU 时间
# DMA 方式
与中断驱动方式相比,DMA 方式(直接存储器存取。主要用于块设备的 I/O 控制)有这样几个改进:
- 数据的传送单位是 "块" 不再是一个字、一个字的传送
- 数据的流向是从设备直接放入内存,或者从内存直接到设备。不再需要 CPU 作为中间传递设备
- 仅在传送一个或多个数据块的开始和结束时,才需要 CPU 干预
CPU 指明此次要进行的操作(如:读操作),并说明读入多少数据、数据要存放在内存的什么位置、数据在外部设备上的地址(如:在磁盘上的地址)
控制器会根据 CPU 提出的要求完成数据的读 / 写工作,整块数据的传输完成后,才向 CPU 发出中断信号
DMA 控制器结构:
- CPU 干预的频率
- 仅在传送一个或多个数据块的开始和结束时,才需要 CPU 干预
- 数据传送的单位:
- 每次读 / 写一个或多个块(注意:每次读写的只能是连续的多个块,且这些块在读入内存后在内存中也必须是连续的)
- 数据的流向:
- 读操作(数据读入):I/O 设备 -> 内存
- 写操作(数据输出):内存 ->I/O 设备
- 主要缺点和优点:
- 优点:数据传输以 "块" 为单位,CPU 介入频率进一步降低。数据的传输不再需要先经过 CPU 再写入内存,数据传输效率进一步增加。CPU 和 I/O 设备的并行性得到提升
- 缺点:CPU 每发出一条 I/O 指令,只能读 / 写一个或多个连续的数据块
- 如果要读 / 写多个离散存储的数据块,或者要将数据分别写在不同的内存区域时,CPU 要分别发出多条 I/O 指令,进行多次中断处理才能完成
# 通道控制方式
通道:一种硬件,可以理解为是 "弱化版的 CPU"(与 CPU 相比,通道可以执行的指令很单一,并且通道程序是放在主机内存中的,也就是说通道与 CPU 共享内存)。通道可以识别并执行一系列通道指令
CPU 向通道发出 I/O 指令。指明通道程序在内存中的位置,并指明要操作的是哪个 I/O 设备。之后 CPU 就切换到其他进程执行了
通道执行内存中的通道程序(其中指明了要读入 / 写出多少数据,读 / 写的数据应放在内存的什么位置等信息)
通道执行完规定的任务后,向 CPU 发出中断信号,之后 CPU 对中断进行处理
CPU 的干预频率:
- 极低,通道会根据 CPU 的指示执行相应的通道程序,只有完成一组数据块的读 / 写后才需要发出中断信号,请求 CPU 干预
数据传送的单位
- 每次读 / 写一组数据块
数据的流向(在通道的控制下进行):
- 读操作(数据读入):I/O 设备 -> 内存
- 写操作(数据输出):内存 ->I/O 设备
主要缺点优点:
- 缺点:实现复杂,需要专门的通道硬件支持
- 优点:CPU、通道、I/O 设备可并行工作,资源利用率很高
# I/O 软件层次结构
# 用户层软件
用户层软件实现了与用户交互的接口,用户可直接使用该层提供的、与 I/O 操作相关的库函数对设备进行操作
用户层软件将用户请求翻译成格式化的 I/O 请求,并通过 "系统调用" 请求操作系统内核的服务
用户层上封装了一系列更方便的库函数接口供用户使用
# 设备独立性软件
设备独立性软件,又被称为设备无关性软件。与设备的硬件特性无关的功能几乎都在这一层实现
主要实现的功能:
- 向上层提供统一的调用接口
- 设备的保护(原理类似于对文件的保护。设备被看成一种特殊的文件,不同用户对各个文件的访问权限是不一样的,同理,对设备的访问权限也不一样)
- 差错处理(设备独立性软件需要对一些设备的错误进行处理)
- 设备的分配与回收
- 数据缓冲区管理(可以通过缓冲技术屏蔽设备之间数据交换单位大小和传输速度的差异)
- 建立逻辑设备名到物理设备名的映射关系:根据设备类型选择调用相应的驱动程序
- 设备独立性软件需要通过 "逻辑设备表" 来确定逻辑设备对应的物理设备,并找到该设备对应的设备驱动程序
- 操作系统可以采用两种方式管理逻辑设备表:
- 第一种:整个系统只设置一张 LUT,这就意味着所有用户不能使用相同的逻辑设备名,因此这种方式只适用于单用户操作系统
- 第二种:为每个用户设置一张 LUT,各个用户使用的逻辑设备名可以重复,适用于多用户操作系统。系统会在用户登录时为其建立一个用户管理进程,而 LUT 就存放在用户管理进程的 PCB 中
# 设备驱动程序
主要负责对硬件设备的具体控制,将上层发出的一系列命令(如 read/write)转化成特定设备 "能听得懂" 的一系列操作。包括设置设备寄存器;检查设备状态等
不同设备的内部硬件特性不同,这些特性只有厂家才知道,因此厂家需要根据设备的硬件特性设计并提供相对应的驱动程序,CPU 执行驱动程序的指令序列,来完成设置设备寄存器,检查设备状态等工作
注:驱动程序一般会以一个独立进程的方式存在
# 中断处理程序
当 I/O 任务完成时,I/O 控制器会发送一个中断信号,系统会根据中断信号类型找到相应的中断处理程序并执行。
总结
直接涉及到硬件具体细节、且与中断无关的操作肯定是设备驱动程序层完成的;没有设计硬件、对各种设备都需要进行的管理工作都是由设备独立性软件层完成的
逻辑设备表作用:建立逻辑设备名到物理设备名的映射关系:根据设备类型选择调用相应的驱动程序
# 输入输出应用程序接口
输入输出应用程序接口是由设备独立软件提供给用户 I/O 软件的,由于设备多种多样,因此需要提供若干类型的接口
字符设备接口
get/input 系统调用:向字符设备读 / 写一个字符
块设备接口
read/write 系统调用:向块设备的读写指针位置读 / 写多个字符;
seek 系统调用:修改读写指针位置
网络设备接口
又称 "网络套字接口"
socket 系统调用:创建一个网络套接字,需指明网络协议(TCP/UDP)
bind:将套接字绑定到某个本地 "端口"
connect:将套接字连接到远程地址
read/write:从套接字读 / 写数据
阻塞 I/O,非阻塞 I/O
阻塞 I/O:应用程序发出 I/O 系统调用,进程需转为阻塞态等待
- 如:字符设备接口 —— 从键盘读一个字符 get
非阻塞 I/O:应用程序发出 I/O 系统调用,系统调用可迅速返回,进程无需阻塞等待
- 如:块设备接口 —— 往磁盘中写数据
# 设备驱动程序接口
不同的操作系统,对设备驱动程序接口的标准各不相同
操作系统规定好设备驱动程序接口标准,各厂商必须按操作系统的接口要求,开发相应设备驱动程序,设备才能被使用