6现代操作系统(第4版)

I/O 系统

与外部世界交互

I/O硬件I/O软件磁盘时钟

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第6章 I/O 系统(与外部世界交互) I/O Systems

一、导读

1.1 本章学习目标

理解 I/O 系统的整体架构,包括硬件组件和软件层次

掌握 I/O 控制的几种方式:轮询、中断驱动、DMA 和通道控制

深入理解 I/O 软件的层次结构及各层的功能职责

掌握磁盘驱动器的物理结构、性能参数和调度算法

理解 RAID 技术的原理、级别及其在可靠性和性能方面的作用

了解设备驱动程序的设计原理和设备独立性软件的功能

掌握时钟设备在操作系统中的关键作用

理解 I/O 缓冲策略和性能优化技术

1.2 为什么本章如此重要

I/O 系统是计算机与外部世界交互的唯一通道。键盘、鼠标、显示器、磁盘、网卡、打印机——所有这些设备都需要通过 I/O 系统与 CPU 和内存进行数据交换。I/O 系统的效率和可靠性直接影响着整个系统的性能表现和用户体验。

从性能的角度来看,I/O 长期以来是系统性能的瓶颈。CPU 的速度遵循摩尔定律持续快速增长,而机械磁盘的寻道时间在数十年间几乎没有实质性改善。即使 SSD 的出现大幅提升了存储 I/O 的速度,网络延迟和带宽限制仍然是分布式系统的关键瓶颈。理解 I/O 系统的运作机制,是进行性能优化和瓶颈分析的基础。

从系统设计的角度来看,I/O 设备的种类繁多、特性各异。键盘是低速的字符设备,磁盘是高速的块设备,网卡是异步的数据包设备,显示器是实时的流设备。操作系统需要为所有这些设备提供统一的接口,同时又要充分利用每种设备的特性来优化性能。I/O 软件的分层设计是软件工程中的经典范例——通过抽象和分层来管理复杂性。

从可靠性的角度来看,I/O 设备是系统中最容易发生故障的组件。磁盘可能出现坏扇区,网络可能丢包,USB 设备可能突然断开。I/O 系统必须具备处理各种异常和故障的能力,通过重试、纠错、冗余等机制保证数据的正确传输。

1.3 核心问题引导

为什么 I/O 操作比内存操作慢几个数量级?这种速度差距如何影响系统设计?

轮询和中断驱动两种 I/O 控制方式各有什么优缺点?

DMA 是如何减少 CPU 在 I/O 操作中的参与的?它的工作过程是什么?

I/O 软件为什么采用分层设计?每一层的职责是什么?

磁盘调度算法如何影响磁盘性能?不同算法适用于什么场景?

RAID 技术如何提高存储系统的可靠性和性能?不同 RAID 级别有什么区别?

设备驱动程序如何屏蔽硬件差异,为上层提供统一接口?

时钟中断在操作系统中扮演了哪些关键角色?


二、核心概念详解

2.1 I/O 硬件原理

I/O 系统由 I/O 设备和控制这些设备的硬件组成。从操作系统的角度来看,I/O 硬件包含几个关键组件。

设备(Device)是实际执行 I/O 操作的物理实体,如磁盘驱动器、网卡、打印机等。设备控制器(Device Controller)是控制设备操作的电子电路,它负责将来自操作系统的高层命令转换为设备能够理解的电信号。每个设备控制器管理一个或多个设备,通过总线(Bus)与 CPU 和内存通信。

设备控制器通常包含一组寄存器(Register)用于与 CPU 通信。数据寄存器(Data Register)用于传输数据;状态寄存器(Status Register)用于指示设备和控制器的当前状态(如忙、就绪、错误);命令寄存器(Command Register)用于接收 CPU 发出的命令。CPU 通过读写这些寄存器来控制设备操作。

总线是连接 CPU、内存和 I/O 设备的通信通道。常见的总线标准包括 PCIe(用于高速设备如显卡和 SSD)、SATA(用于磁盘驱动器)、USB(用于外设)和网络总线(如 Ethernet)。总线的带宽和延迟直接影响 I/O 性能。

I/O 地址的编址方式有两种。内存映射 I/O(Memory-Mapped I/O)将设备寄存器的地址映射到内存地址空间中,CPU 使用普通的内存访问指令(LOAD/STORE)来读写设备寄存器。这种方式的优点是可以使用相同的指令集,简化了编程。独立 I/O(I/O-Mapped I/O)使用专门的 I/O 指令(如 x86 的 IN/OUT 指令)和独立的 I/O 地址空间。这种方式不占用内存地址空间,但需要额外的指令集支持。

2.2 I/O 控制方式

I/O 控制方式决定了 CPU 与 I/O 设备之间的协作模式,经历了从低效到高效的演进过程。

程序控制 I/O(Programmed I/O,也称轮询方式)是最简单的方式。CPU 向设备控制器发送命令后,不断轮询设备状态寄存器,等待操作完成。在等待期间,CPU 被完全占用,无法执行其他任务。这种方式仅适用于极低速的设备或嵌入式系统。

中断驱动 I/O(Interrupt-Driven I/O)解决了 CPU 空等的问题。CPU 向设备发送命令后继续执行其他任务,设备完成操作后通过中断通知 CPU。中断处理程序读取数据并处理结果。这种方式在设备速度较慢时非常高效,但对于高速设备的大量数据传输,频繁的中断仍然会给 CPU 带来较大负担——每个字节或每个数据块都需要一次中断。

DMA(Direct Memory Access,直接内存访问)进一步减少了 CPU 的参与。DMA 控制器(DMAC)作为总线上的独立设备,可以在设备和内存之间直接传输数据块,无需 CPU 逐字节干预。CPU 只需初始化 DMA 传输(指定源地址、目标地址和传输长度),然后继续执行其他任务。DMA 传输完成后,DMA 控制器发出一个中断通知 CPU。DMA 使得高速设备(如磁盘和网卡)能够以接近硬件极限的速度传输数据。现代 DMA 控制器支持总线掌控权(Bus Mastering),可以主动获取总线控制权进行传输。

通道控制(Channel I/O)是更高级的 I/O 方式,主要见于大型机系统。通道是一种专用的 I/O 处理器,能够执行简单的 I/O 程序(Channel Program),自主完成复杂的 I/O 操作序列。CPU 只需向通道提交一个 I/O 程序,通道就能独立完成多个设备的多个操作。这种方式将 I/O 处理从 CPU 进一步解放出来。

2.3 I/O 软件层次结构

I/O 软件采用分层设计,从最接近硬件的底层到最接近用户的顶层,每一层向上层提供抽象接口,隐藏下层的实现细节。典型的层次结构包括四层。

中断处理程序(Interrupt Handler)位于最底层,负责处理硬件中断。当 I/O 设备完成操作发出中断时,中断处理程序被调用,读取设备状态和数据,唤醒等待该 I/O 完成的进程。中断处理程序必须尽可能短小快速,因为它在中断禁用状态下运行。

设备驱动程序(Device Driver)是操作系统中唯一了解特定硬件细节的部分。每种设备类型都有对应的驱动程序,它封装了设备的寄存器操作、命令序列和错误处理逻辑。驱动程序向上层提供统一的接口(如 read、write、ioctl),屏蔽了不同硬件的差异。驱动程序通常作为可加载模块(Kernel Module)动态加载,使得操作系统无需重新编译即可支持新设备。

设备独立性软件(Device-Independent Software)提供与设备无关的通用功能。它包括统一的设备命名和访问接口、设备保护(权限检查)、缓冲区管理、块设备与字符设备的统一抽象等。设备独立性软件使得上层应用可以使用相同的系统调用来操作不同类型的设备。

用户级 I/O 软件位于最上层,包括系统调用接口(如 POSIX I/O)、标准库函数(如 C 的 stdio)和高级 I/O 库。这一层提供面向用户的编程接口,如格式化 I/O(printf/scanf)、缓冲 I/O 和高级文件操作。

2.4 磁盘管理与调度

磁盘是计算机系统中最重要的持久存储设备,其性能特征和管理策略对系统整体性能有着深远的影响。

机械硬盘(HDD)的物理结构包括盘片(Platter)、磁头(Head)、磁道(Track)和扇区(Sector)。磁盘读写操作的时间由三部分组成:寻道时间(Seek Time)——磁头移动到目标磁道所需的时间,通常为 3-15ms;旋转延迟(Rotational Latency)——等待目标扇区转到磁头下方所需的时间,平均为盘片旋转半圈的时间(7200RPM 的磁盘约为 4.2ms);传输时间(Transfer Time)——数据从磁盘读出或写入的时间,取决于数据量和磁盘转速。

由于寻道时间远大于其他操作,磁盘调度算法的核心目标是 minimize 平均寻道时间。先来先服务(FCFS)按请求到达顺序处理,公平但性能差。最短寻道时间优先(SSTF)优先处理距离当前磁头位置最近的请求,减少了平均寻道时间但可能导致远端请求饥饿。SCAN 算法(电梯算法)让磁头在一个方向上移动,处理沿途的所有请求,到达末端后反向移动。C-SCAN(循环 SCAN)在反向移动时不处理请求,直接回到起点,提供了更均匀的等待时间。LOOK 和 C-LOOK 是 SCAN 和 C-SCAN 的改进版本——磁头不需要移动到磁盘的物理末端,而是在最远的请求处就转向。

RAID(Redundant Array of Independent Disks)技术通过组合多个磁盘来提高存储系统的性能和可靠性。RAID 0 使用数据条带化(Striping)将数据分散存储在多个磁盘上,提高了读写吞吐量但不提供冗余。RAID 1 使用镜像(Mirroring),每个磁盘的数据都在另一个磁盘上有完整副本,提供了最高的可靠性但空间利用率只有 50%。RAID 5 使用分布式奇偶校验,数据和校验信息均匀分布在所有磁盘上,可以容忍一个磁盘故障,空间利用率为 (n-1)/n。RAID 6 类似 RAID 5 但使用双重奇偶校验,可以容忍两个磁盘同时故障。


三、深入分析与讨论

3.1 I/O 性能瓶颈与优化

I/O 性能是现代计算系统中最常见的瓶颈之一。理解 I/O 性能的限制因素和优化策略,对于系统设计和性能调优至关重要。

I/O 性能的根本限制在于设备速度与 CPU/内存速度之间的巨大差距。CPU 的时钟周期约为 0.3ns,内存访问延迟约为 100ns,而 SSD 的读取延迟约为 50-100us,机械磁盘的访问延迟约为 5-10ms。这种速度差距达到了 5-7 个数量级。即使 SSD 相比 HDD 有了巨大提升,但与内存相比仍然慢了 3 个数量级。

缓冲(Buffering)是最基本的 I/O 优化策略。在内存中设置缓冲区,将多次小的 I/O 操作合并为少量大的操作。写缓冲(Write Buffering)将多个写请求合并后一次性写入设备;读缓冲(Read Buffering)预读数据到内存中,后续请求可以直接从缓存获取。双缓冲(Double Buffering)使用两个交替的缓冲区,一个用于数据传输,另一个用于数据处理,实现流水线化操作。

异步 I/O(Asynchronous I/O)允许进程发起 I/O 操作后立即继续执行,不等待 I/O 完成。当 I/O 完成时,通过信号、回调或事件通知机制告知进程。Linux 的 io_uring 接口和 Windows 的 IOCP(I/O Completion Port)都是高效的异步 I/O 实现。异步 I/O 使得单个线程可以同时处理大量并发的 I/O 操作,是高并发服务器的基础。

I/O 多路复用(I/O Multiplexing)允许单个线程同时监控多个 I/O 描述符的状态。select()、poll() 和 epoll() 是 Linux 中的三种 I/O 多路复用机制。epoll() 通过事件驱动的方式避免了 select() 的线性扫描开销,是构建高性能网络服务器的核心技术。Nginx、Redis 等高性能服务器都基于 epoll 实现。

3.2 设备驱动程序的架构

设备驱动程序是操作系统中与硬件直接对话的软件组件,其设计质量直接影响设备的性能和稳定性。

Linux 的设备模型围绕三个核心概念构建:设备(Device)、驱动程序(Driver)和总线(Bus)。设备代表一个物理或虚拟的硬件实体;驱动程序包含操作设备的代码;总线是设备和驱动程序之间的匹配机制——当一个新设备被检测到时,总线会尝试将设备与匹配的驱动程序绑定。

Linux 将设备分为三大类。字符设备(Character Device)以字节流为单位进行数据传输,如键盘、串口和终端。字符设备的读写操作通常不支持随机访问。块设备(Block Device)以固定大小的块(通常为 512 字节或 4KB)为单位进行数据传输,如磁盘和 SSD。块设备支持随机访问,是文件系统的基础。网络设备(Network Device)通过网络协议栈进行数据传输,不使用传统的 read/write 接口,而是通过 Socket API 和 sk_buff 数据结构。

设备树的引入(特别是嵌入式系统中的 Device Tree 和 ACPI)进一步简化了设备管理。设备树将硬件配置信息从驱动程序代码中分离出来,以数据结构的形式描述硬件的拓扑关系和配置参数。这使得同一个驱动程序可以支持不同硬件平台上的同类设备,提高了代码的可移植性。


四、实践案例

4.1 Linux I/O 调度器实践

Linux 内核提供了多种 I/O 调度器,适用于不同的设备类型和工作负载。

CFQ(Completely Fair Queuing)是传统的默认调度器,为每个进程维护独立的 I/O 队列,轮流分配时间片,保证 I/O 带宽的公平分配。CFQ 适合多用户桌面环境,但在服务器场景中可能不是最优选择。

Deadline 调度器为每个 I/O 请求设置截止时间,优先处理即将超时的请求,防止请求饥饿。Deadline 在数据库服务器等对延迟敏感的场景中表现良好。

NOOP 调度器几乎不做任何调度工作,只是简单地将请求合并后按 FIFO 顺序提交。NOOP 适合 SSD 等没有寻道开销的设备,因为磁盘调度对 SSD 没有意义,反而增加了不必要的开销。

BFQ(Budget Fair Queueing)是 CFQ 的改进版本,基于带宽分配而非时间片分配来实现公平性。BFQ 在多媒体和实时应用中表现优异。

通过 /sys/block/[device]/queue/scheduler 文件可以查看和切换 I/O 调度器。对于 SSD 设备,通常推荐使用 none(NOOP)或 mq-deadline;对于机械磁盘,bfq 或 kyber 可能是更好的选择。

4.2 DMA 在磁盘传输中的应用

DMA 是现代 I/O 系统的基石,以磁盘数据传输为例可以清晰地理解其工作原理。

当应用程序请求读取磁盘文件时,CPU 首先通过设备驱动程序向磁盘控制器发送读命令,指定要读取的扇区号和数量,以及数据在内存中的目标地址。然后 CPU 将总线控制权交给 DMA 控制器,自己继续执行其他任务。DMA 控制器接管总线后,等待磁盘控制器将数据从磁盘读出并放入控制器的缓冲区,然后将数据通过总线传输到指定的内存地址。当所有数据传输完成后,DMA 控制器向 CPU 发出中断信号。CPU 的中断处理程序检查传输结果,唤醒等待数据的进程。

整个过程中,CPU 只参与了命令发送和结果处理两个阶段,实际的数据传输完全由 DMA 控制器和磁盘控制器在硬件层面完成。这极大地释放了 CPU 资源——一个 100MB/s 的磁盘传输在 DMA 模式下几乎不消耗 CPU 周期,而在程序控制 I/O 模式下则需要 CPU 全速运行。

现代系统使用 Scatter-Gather DMA(SGDMA)进一步提高了效率。传统的 DMA 要求目标内存区域连续,而 SGDMA 允许将数据分散传输到多个不连续的内存区域。驱动程序构建一个 SG 描述符列表,每个条目指定一个内存地址和长度,DMA 控制器按列表依次传输。这避免了在传输前将数据合并到连续缓冲区的开销。


五、常见误区与注意事项

误区:SSD 完全不需要 I/O 调度。 虽然 SSD 没有机械寻道开销,但 I/O 调度仍然有其价值——请求合并可以减少 I/O 操作次数,公平调度可以防止某些进程垄断 I/O 带宽。只是调度的重点从寻道优化转向了队列深度管理和延迟控制。

误区:DMA 传输完全不消耗 CPU 资源。 DMA 减少了 CPU 在数据传输中的参与,但初始化 DMA 传输、处理 DMA 完成中断、进行缓存一致性维护(如 Cache Flush/Invalidate)仍然需要 CPU 参与。

误区:RAID 可以替代备份。 RAID 提供的是冗余而非备份。RAID 可以容忍磁盘硬件故障,但无法防范数据损坏、误删除、恶意攻击和灾难性事件。定期备份仍然是数据安全的必要措施。

注意事项:I/O 密集型应用应优先选择异步 I/O。 同步 I/O 在等待设备响应时阻塞线程,导致 CPU 资源浪费。异步 I/O 允许线程在等待 I/O 时处理其他任务,显著提高资源利用率。

注意事项:USB 设备的热插拔需要特殊处理。 设备可能在任何时刻被拔出,驱动程序必须能够优雅地处理这种意外事件,避免系统崩溃或数据损坏。


六、本章小结

本章系统讲解了 I/O 系统的架构、原理和管理策略。I/O 硬件由设备、设备控制器和总线组成,通过内存映射或独立编址与 CPU 通信。I/O 控制方式从低效的轮询发展到中断驱动、DMA 和通道控制,逐步减少了 CPU 在 I/O 操作中的参与。I/O 软件采用分层设计,从底层的中断处理到上层的用户接口,每层向上提供抽象接口。磁盘作为最重要的持久存储设备,其调度算法(SCAN、C-SCAN、LOOK 等)直接影响系统性能。RAID 技术通过条带化、镜像和奇偶校验来提高存储系统的性能和可靠性。设备驱动程序封装了硬件细节,设备独立性软件提供了统一的设备访问接口。缓冲、异步 I/O 和 I/O 多路复用是优化 I/O 性能的关键技术。理解 I/O 系统的运作机制,对于系统性能调优和设备驱动开发具有重要意义。


七、思考题与练习

比较程序控制 I/O、中断驱动 I/O 和 DMA 三种方式的 CPU 利用率。在什么场景下每种方式最合适?

假设磁盘有 200 个磁道(0-199),磁头当前在 100 号磁道,且刚完成了 95 号磁道的请求。待处理的请求队列为:55, 58, 39, 18, 90, 160, 150, 38, 180。分别使用 FCFS、SSTF、SCAN 和 C-SCAN 算法计算磁头移动的总距离。

比较 RAID 0、RAID 1、RAID 5 和 RAID 6 在性能、可靠性和空间利用率方面的差异。为一个需要高可靠性的金融数据库系统推荐 RAID 配置。

研究 Linux 的 io_uring 接口。它相比传统的 epoll 有什么优势?它是如何实现零拷贝和高效率的?

解释设备驱动程序中"上半部"和"下半部"的分离策略。为什么需要这种分离?Linux 中有哪些实现下半部的机制(如 tasklet、workqueue、softirq)?

分析一个 USB 存储设备从插入到可用的完整过程。涉及哪些硬件检测、驱动程序加载和文件系统挂载步骤?

设计一个实验来测量磁盘的顺序读写速度和随机读写速度。分析两者的差异及其原因。SSD 的实验结果会有何不同?

研究 NVMe(Non-Volatile Memory Express)协议的设计特点。它是如何针对 SSD 和 PCIe 总线进行优化的?与传统 SATA 接口相比有哪些优势?

6/8I/O
0%