整理《Operating System Concepts》 第七版第十三章 I/O 输入系统部分,内容均为原书和中文版翻译的摘录,其中原书摘录部分由我 按个人理解简化、翻译为中文,可能存在一些不准确之处 。
注:整理只包含第 13 章内核 I/O 子系统之前的内容,考试涉及整章内容。
I/O 硬件
- 设备驱动程序(device drivers) 为 I/O 子系统提供了统一设备的访问接口。
- 设备和计算机的通信通过 端口(port) ,一组被一个/多个设备共同使用的线称为 总线(bus) 。总线是一组线和一组严格定义的描述在线上传输信息的协议。 链环(daisy chaine) 形容的是多个设备相连,最终设备通过端口连接到计算机上的模式。链环常常按总线方式工作,一个典型的 PC 总线结构如下图。
- 上图中包含一个 PCI 总线 (最常用的 PC 系统总线)用于连接 CPU 和内存子系统/快速设备, 扩展总线(expansion bus) 用于连接串/并行端口和相对慢的设备(键盘)。
- 控制器(controller) 是用来操作端口、总线或者设备的一组电子器件,它的复杂程度和传输协议有关,如串行端口控制器比较简单,而 SCSI 总线控制器常实现为一个和计算机相连的独立的 主机适配器(host adapter) ,这个适配器会有处理器、微码以及一定的私有内存,从而能够处理 SCSI 协议信息。
- 控制器有一个/多个用于数据和控制信号的寄存器, 处理器通过读写这些寄存器来实现与控制器的通信 。这种通信的可以通过特殊的 I/O 指令向指定的 I/O 端口地址传输一个字节/字,也可以通过 内存映射 I/O 模式(在 虚拟内存 中介绍过),处理器能够通过标准数据传输指令完成对控制器的读写。部分系统同时采用这两种方式,例如图像控制器有 I/O 端口来完成基本控制操作,还有一个较大的内存映射区域来支持屏幕内容的接收和生成。
- I/O 端口通常有 4 种寄存器,寄存器通常为 1 ~ 4B:状态寄存器、控制寄存器、数据输入寄存器和数据输出寄存器。有的控制器有 FIFO 芯片从而可以保留多个输入/输出数据。上述四种寄存器的主要功能有:
- 主机从 数据输入寄存器 读出数据
- 主机向 数据输出寄存器 写入数据
- 主机可从 状态寄存器 读出设备当前的状态
- 主机向 控制寄存器 写入数据来发送命令、改变设备状态
轮询
- 主机和控制器之间交互很复杂,但基本的 握手(handshaking) 比较简单。假设控制器的状态寄存器中有一位用于说明设备当前是否在忙,控制器正忙时就将这一位置位。控制器的命令寄存器中有一位说明主机是否有任务准备就绪,当主机需要控制器执行某个操作时,需要将命令寄存器的这一位置位。主机和控制器交互输出一个字节时的握手流程如下:
- 主机不断读取状态寄存器,直到状态寄存器中的 忙位 为 0
- 主机设置命令寄存器中的 写位 并把一个字节写到数据输出寄存器
- 主机设置命令寄存器中的 就绪位
- 控制器注意到命令寄存器中的就绪位被置位,因此将状态寄存器中的 忙位 置位
- 控制器读取命令寄存器并发现 写位 被置位,因此了解到需要执行一条写命令。它从数据输出寄存器读出一个字节,并向设备执行 I/O 操作
- 控制器操作完成后将命令寄存器中的 就绪位 清除,并清除状态寄存器中的 故障位 (这说明 I/O 设备成功完成任务),最后清除状态寄存器中的 忙位 表示本次字节传输操作结束
- 在步骤 1 中主机将处于 忙等待(busy-waiting) 或者 轮询(polling) 状态。多数计算机体系只需要三个 CPU 指令周期就可以完成基本的轮询操作,但不断地重复轮询会浪费处理器资源。
中断
- 中断(interrupt) 是使外设通知 CPU 的硬件机制。CPU 硬件有一条 中断请求线(Interrupt-request line,IRL) ,CPU 执行完每条指令都会检测 IRL 判断是否有控制器通过 IRL 发送了信号。如果有,CPU 会保存当前的状态并且跳转到 中断处理程序(interrupt-handler) 。中断处理程序会判断中断原因、进行处理、恢复状态并执行中断返回指令使 CPU 返回中断之前的执行状态。整个流程大致为:
- 设备控制器通过中断请求线 发送中断信号引起(raise)中断
- CPU 捕获(catch)中断并分发(dispatch)到中断处理程序
- 中断处理程序处理设备请求以 清除中断
- CPU 和 中断控制器(interrupt-controller) 硬件提供了以下三个特性:
- 在 CPU 执行关键指令时可以延迟对中断的处理
- 能够将中断快速转发给适当的中断处理程序,而不必检查所有设备以确定是哪个设备引发了中断
- 支持多级中断,可以根据紧迫性来响应中断
- 多数 CPU 有两个中断请求线: 非屏蔽中断(nonmaskable) 用于处理非常严重的,不可以恢复的内存错误等问题, 可屏蔽中断(maskable) 可被设备控制器用来请求服务,如果 CPU 正在执行关键、不可中断指令,则可以屏蔽这一类中断线上的请求。
- 中断机制根据 中断向量(interrupt vector) 来选择中断服务程序。中断向量和中断服务程序被维护在一张表中,中断向量支持的地址数量有限(例如 8 位中断向量只能对应 256 个中断服务程序,奔腾即为 256 个中断向量,0~31 用于各种错误等非屏蔽中断,剩下的为可屏蔽中断), 中断链接(interrupt chaining) 可解决这个问题:中断向量指向的不再是单一的中断服务程序,而是一个中断服务程序的链表,中断一旦发生,对应链表中的全部中断处理程序都会一一调用,直到发现了能够处理请求的中断服务程序为止。
- 中断优先级(interrupt priority) 使 CPU 可以在不屏蔽所有中断的情况下延迟处理低优先级的中断,并且也允许高优先级的中断抢占低优先级的中断处理。
- 现代操作系统启动时会探查硬件总线、确定哪些设备存在并将对应的中断处理程序安装到中断向量中。操作系统对于中断机制的应用非常广泛:
- 设备控制器通过中断表明自己已经准备好服务
- 通过中断机制处理例如被 0 除、违例内存访问等 异常(Exception)
- 使用中断进行虚拟内存分页,页错误会引发中断异常,这个中断会挂起当前进程并跳转到内核的页错误处理程序
- 程序执行系统调用会触发 软中断(software interrupt) 或者 陷阱指令(trap)
直接内存访问
- 使用通用处理器不断监听设备控制器的寄存器并按字节传输( 程序控制 I/O ,Programmed I/O,PIO)是对计算资源的非常过分的浪费。计算机为了避免 PIO 增加 CPU 负担,将一部分数据传输任务交付 直接内存访问(direct-memory access,DMA) 控制器。
- 开始 DMA 传输时,主机向内存写入 DMA 命令块,块中包含传输的源、目的地址指针以及传输的字节数。 CPU 将该命令块的地址写到 DMA 控制器中 并继续其他工作,DMA 控制器会根据命令块直接操作内存总线完成传输(这段时间 CPU 无法使用总线)。传输完成后 DMA 控制器会中断 CPU 并交还给 CPU 总线控制权。
- DMA 和设备控制器之间的握手通过 DMA-request 和 DMA-acknowledge 线进行,设备有数据需要传输时,设备控制器就通过 DMA-request 线通知 DMA 控制器,DMA 控制器会发出申请中断 CPU,在从 CPU 获取所需要的地址后将地址放到内存地址总线上,并通过 DMA-acknowledge 线通知设备控制器。设备控制器收到这个信号,向内存地址总线上的地址写入数据。交互过程如下图。
- DMA 控制总线传输期间 CPU 不能访问主存(仍可访问 L1、L2 缓存),这称为 周期挪用(cycle stealing) ,会放慢 CPU 计算,但往往能够改善系统总体性能。有的 DMA 使用物理内存地址,有的使用虚拟内存地址(这时候需要有一个虚拟到物理地址的转换),使用虚拟内存地址的 DMA 称为 直接虚拟内存访问(direct virtual-memory access,DVMA) 。DVMA 可以直接实现两个内存映射设备之间的传输而无需 CPU 干涉。
I/O 应用接口及后面几节简单摘要
- 设备在很多方面有很大差异:
- 字符流或块:字符流设备按字节传输,块设备以块为单位传输
- 顺序访问或随机访问
- 同步或异步:同步设备按照一定响应时间进行数据传输,异步设备则呈现无规则/不可预测的响应时间
- 共享或专用:共享设备可以被多个进程/线程并发使用,专用设备则不可以
- 操作速度:设备速度不同
- 读写/只读/只写:设备支持的数据传输方向不同
- 块设备(block-device) 接口规定了访问磁盘驱动器以及其它块设备所需的各个方面。操作系统本身和特殊的应用程序(如数据库)倾向于将块设备当作简单的线性块数组访问,这种访问方式称为 原始(raw) I/O 。
- 阻塞和非阻塞 I/O :
- 应用程序发出 阻塞(blocking) I/O 类型的系统调用时,应用程序就会被挂起,移动到进程等待队列中。因为阻塞式的 I/O 容易理解,并且 I/O 设备执行所需的时间是异步的,执行时间不可预估,因此绝大多数操作系统给应用程序预留的接口都是阻塞系统调用。
- 有的用户级进程需要 非阻塞(nonblocking) I/O ,例如用户接口,它用来接收键盘/鼠标输入,同时还要在屏幕回显。又或者视频应用程序,它需要从磁盘读取帧并解码到显示器上。非阻塞 I/O 通常使用多线程实现,有的线程执行阻塞系统调用,其他线程继续执行。
- 异步系统调用(asynchronous system call) 不必等待 I/O 完成就可以立刻返回,应用程序继续执行。I/O 完成时会通知应用程序,比如设置程序空间里某个变量,或者触发信号/软件中断等。
- 缓冲区(buffer) 是用来保存两个设备之间或者设备和应用程序之间传输数据的内存区域。采用缓冲的理由有:
- 处理数据流的生产者与消费者之间的速度差异
- 协调传输数据大小不一致的设备
- 支持程序 I/O 的复制语义
- I/O 内核子系统 (kernel’s I/O subsystem)提供了很多和 I/O 有关的服务,包括:
- 调度(scheduling)
- 缓冲(buffering)
- 高速缓存(caching)
- 假脱机(spooling)
- 设备预留(device reservation)
- 错误处理(error handling)
- 名称转换(name translation)
专栏目录:计算机理论基础
此专栏的上一篇文章:操作系统(十二):大容量存储器结构
此专栏的下一篇文章:操作系统(专题):信号量编程
参考资料:《操作系统概念 英文第七版》,恐龙书,英文名《Operating System Concepts》,作者 Abraham Silberschatz、Peter Baer Galvin、Greg Gagne
原创作品,允许转载,转载时无需告知,但请务必以超链接形式标明文章原始出处(http://blog.forec.cn/2017/01/06/os-concepts-13/) 、作者信息(Forec)和本声明。