I/O 控制方式与驱动模型:轮询、中断、DMA 与 VFS
CPU 与设备交互有三种基本控制方式——轮询(忙等)、中断(事件驱动)、DMA(设备直搬内存),上层通过驱动程序与 VFS 统一接口屏蔽千差万别的硬件。
一句话定义
CPU 与设备交互有三种基本控制方式——轮询(忙等)、中断(事件驱动)、DMA(设备直搬内存),上层通过驱动程序与 VFS 统一接口屏蔽千差万别的硬件。
为什么重要
I/O 是操作系统"管理外部世界"的全部内容。三种控制方式的取舍贯穿一切性能设计:网卡高吞吐场景为什么回归轮询(DPDK)、NVMe 为什么要海量中断聚合、数据库为什么绕开页缓存直写设备。理解它们,kp-028 的 I/O 模型演进才有因果链。
前置知识
kp-002(中断控制器与 DMA 的硬件机制)。
核心概念
- 轮询(programmed I/O):CPU 反复读设备状态寄存器,简单、延迟低、纯浪费 CPU。
- 中断驱动:设备就绪时发中断,CPU 现场被保存后执行处理程序。
- 中断下半部:上半部(硬中断,越短越好)登记数据即返回,耗时工作推迟到 softirq/工作队列。
- DMA:控制器自主搬运,CPU 只参与设置与收尾。
- 设备控制器与寄存器:状态/命令/数据寄存器,端口 I/O 或内存映射(MMIO)访问。
- 驱动程序(driver):内核中按总线枚举、以统一操作表注册的硬件专用代码。
- VFS 与字符/块设备:一切设备在文件树上有节点,读写走统一系统调用。
原理与机制
三种方式的适用矩阵:
| 方式 | CPU 占用 | 延迟 | 适用 |
|---|---|---|---|
| 轮询 | 100% 忙等 | 最低 | 高吞吐包处理(DPDK)、自旋短等待 |
| 中断 | 事件触发 | 中(切换开销) | 通用低速设备、键盘 |
| 中断+DMA | 极低 | 中 | 磁盘、网卡、显卡大块传输 |
一次磁盘读的中断式流程:驱动下发命令(写命令寄存器)→ 设置 DMA 描述符 → 睡眠等待 → 设备搬完数据发中断 → 上半部确认中断、登记完成 → 下半部唤醒等待进程。上半部/下半部分离是中断工程的灵魂:硬中断处理里若做重活,中断会被长时间屏蔽,系统整体延迟劣化。
VFS 的统一:open/read/write/close 背后是一张 file_operations 操作表——ext4 的读写指向页缓存逻辑,串口的读写指向驱动收发函数,应用层完全无感。这就是"一切皆文件"的工程兑现。
图示
PIO 路径: CPU <--状态寄存器轮询--> 设备 CPU 全程守候
中断+DMA: CPU 设置 -> 设备 --DMA--> 内存 ; 完成 -> 中断 -> CPU 收尾
CPU 期间可运行其他进程
直观类比
轮询像站在门口每秒看一次信箱;中断像留了门铃;DMA 则像快递员直接进储藏室上架,只在搬完时按一次门铃。上半部/下半部像前台只签收包裹(硬中断),拆包入库交给后台仓管(工作队列)。
实例或案例
cat /proc/interrupts # 各 CPU 核的中断计数与设备分布(含中断亲和)
watch -n1 'cat /proc/interrupts | grep eth0' # 观察网卡中断风暴
lspci -k # 设备与正在使用的驱动
udevadm info /dev/sda # 设备的 udev 属性(设备模型的用户态面)
ethtool -S eth0 | head # 网卡计数器, 可见中断聚合与丢包
高吞吐观察:小包 flood ping 时 cat /proc/interrupts 中网卡中断线性飙升——这正是 NAPI 采用"中断 + 轮询混合"(首包中断后转轮询收包)的动机。
常见误区
- 认为中断永远优于轮询:每秒百万包时每包一次中断的切换成本吞掉全部算力,高性能网络以受控轮询为正解。
- 在中断处理程序里做耗时操作或睡眠:硬中断上下文禁止睡眠,且会屏蔽同级中断,重活必须推迟到下半部。
- 认为驱动必然在内核态:用户态驱动框架(UIO、DPDK、VFIO)把设备直接映射给应用,是现代高性能 I/O 的主流路径之一。
与其他知识点的关系
DMA 与页的配合连接 kp-017( bounce buffer 与对齐);中断上下文不可睡眠约束了 kp-012 内核自旋锁的使用场景;请求队列的中断唤醒是 kp-013 的实例。
延伸阅读
《现代操作系统》I/O 章;《Linux 设备驱动程序》(LDD3,虽旧但上半/下半部模型讲得最透)。
自测题
- 为什么中断处理要分上半部与下半部?
答:硬中断需尽快返回以恢复系统响应性,上半部只做确认与登记,耗时处理推迟到可调度、可睡眠的下半部执行。
- 什么场景应该选轮询?
答:事件到达率高且持续(高吞吐包处理)时,轮询省掉每事件的中断切换成本,代价是占满一个核——用专用核换延迟与吞吐。
- VFS 解决什么问题?
答:为 ext4、NFS、procfs、设备节点等异构实现提供统一的 open/read/write 接口,使"一切皆文件"成为应用可依赖的稳定契约。