跳到主要内容

I/O 系统:从设备驱动程序到异步流

I/O 系统是高速、结构化的 CPU/RAM 世界与混乱、异步的外部硬件世界之间的桥梁。管理从超高速 NVMe 驱动器到慢速人机交互设备的各种硬件,需要一个灵活且健壮的内核架构。

本章探讨内核如何管理设备通信、优化数据吞吐量,并为多种多样的硬件提供统一的接口。


1. 硬件通信原语

CPU 使用几种底层机制与硬件控制器进行对话。

1.1 内存映射 I/O (MMIO)

现代 CPU 为硬件设备保留了一部分物理地址空间。

  • 概念:写入地址 0xFFFF0000 可能并不会写入 RAM,而是向网卡发送了一条指令。
  • 收益:CPU 可以使用标准的加载/存储指令与硬件交互。

1.2 中断:异步信号

CPU 不再不断地检查(轮询)设备是否就绪,而是由设备主动向 CPU 发出信号。

  • 中断向量中断描述符表 (IDT) 中的索引。
  • 屏蔽:CPU 可以临时禁用中断,以保护关键的内核代码。

1.3 直接存储器访问 (DMA) 与 IOMMU

通过 CPU 传输数兆字节的数据是对周期的极大浪费。

  • DMA 控制器:一个专门的硬件单元,可以在没有 CPU 干预的情况下,直接将数据从设备(如磁盘)移动到 RAM。
  • IOMMU:类似于 MMU,但针对 I/O 设备。它确保设备只能访问内核显式分配给它的内存区域,提供硬件级的保护。

2. 内核 I/O 架构

2.1 设备驱动模型

设备驱动程序是连接内核通用 I/O 调用与硬件特定寄存器之间的软件组件。

  • 字符设备:处理字节流(键盘、鼠标、串口)。
  • 块设备:以可寻址块的形式处理数据(HDD, SSD, Flash)。
  • 网络设备:处理离散的数据包。

2.2 上半部与下半部 (Linux)

处理中断必须很快,因为在此过程中其他中断是被禁用的。

  • 上半部 (ISR):立即响应。它确认中断、保存关键数据,并调度“下半部”。
  • 下半部 (Tasklets/Workqueues):异步执行繁重的工作(如解析网络包或复制数据)。

3. I/O 栈与调度

3.1 块 I/O 层

当文件系统请求一个块时,它进入块层

  • 合并 (Merging):如果对 10-15 号块和 16-20 号块的请求同时到达,内核会将它们合并为一个 10 块的请求。
  • 排序 (Sorting):内核对请求进行排序,以最小化磁盘头的寻道时间。

3.2 I/O 调度器

  1. Deadline:通过为每个请求设置严格的过期时间,优先避免进程饥饿。
  2. BFQ (预算公平队列):一种复杂的调度器,试图给予每个进程公平的磁盘带宽份额。非常适合桌面响应速度。
  3. Kyber:一种针对超高速 NVMe 设备设计的现代轻量级调度器。

4. 性能:缓冲与缓存

4.1 双缓冲 (Double Buffering)

当设备正在填充一个缓冲区时,应用程序正在从第二个缓冲区读取。这隐藏了设备的延迟。

4.2 零拷贝 (Zero-Copy - 性能优化)

传统的 read() 后接 write() 到套接字涉及 4 次上下文切换和 4 次数据拷贝。

  • sendfile() / splice():这些系统调用允许内核直接将数据从页缓存传输到套接字缓冲区,避免了任何进入用户态 RAM 的拷贝。

5. 现代高性能 I/O

5.1 I/O 多路复用:epoll

Web 服务器如何处理 10 万个连接?

  • select/poll:服务器询问内核:“这 1000 个套接字中哪些就绪了?”内核每次都要重新扫描全部 1000 个 (O(N)O(N))。
  • epoll (Linux):内核维护一个就绪套接字列表。当套接字变就绪时,它被加入“就绪列表”。服务器仅处理已就绪的套接字 (O(1)O(1))。

5.2 io_uring:新前沿

io_uring 是 Linux 的一个接口,它在用户态和内核态之间使用共享内存环形缓冲区。

  • 提交队列 (SQ):用户态添加 I/O 请求。
  • 完成队列 (CQ):内核添加结果。
  • 结果:一旦环设置完成,提交 I/O 无需发起系统调用,为数据库和网络代理带来巨大的性能提升。

6. 设备抽象:ioctlmmap

6.1 ioctl (输入/输出控制)

一个“包罗万象”的系统调用,用于不属于 read/write 的设备特定操作(如弹出 CD-ROM 或设置串口速度)。

6.2 针对设备的 mmap

高速设备(如显卡)可以将其内部内存直接映射到应用程序的虚拟地址空间。写入特定的内存地址即可直接更新屏幕像素。


7. 故障排除与工具

工具关注点关键洞察
iostat系统级磁盘吞吐量与延迟
iotop进程级哪个应用在“霸占”磁盘?
lsblk结构列出所有块设备和分区
dmesg内核级设备驱动错误和硬件日志
tcpdump网络 I/O在内核级别捕获原始数据包

8. 核心概念复习清单

  • 解释 IOMMU 在现代系统中的作用。
  • 字符设备和块设备有什么区别?
  • 为什么现代内核将中断处理分为上半部和下半部?
  • io_uring 如何减少高频 I/O 的开销?
  • 追踪一个数据包从网线到用户态应用的过程。

第 06 章结束。继续前往:第 07 章:存储系统