ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

操作系统I/O控制方式全解析:从轮询到通道控制的演进与对比

操作系统I/O控制方式全解析:从轮询到通道控制的演进与对比 搞操作系统这几年我越来越觉得I/O控制方式这一块是整本教材里性价比最高的话题内容不多规律极强可一旦真的吃透后面的中断、调度、内存管理很多现象你一眼就能看穿。第一次让我正视这套知识是自己在调一个虚拟机性能时的经历——一个磁盘读请求从发出到返回耗时接近1毫秒而为了等它处理器核心在轮询循环里空转了上百万个时钟周期。那个数字不大但足够震撼原来I/O控制方式的选择直接决定了整个机器的响应速度而不只是教科书上的一个章节。做技术的人常把“把CPU从I/O里解放出来”挂在嘴边但要真说清每一层解放是怎么发生的很多人又容易绕晕。这篇文章我就从程序直接控制讲到中断驱动、DMA、通道控制结合我实际调试和备课的经验把四种方式放在同一条主线上对比顺便说说考试和面试里最常挖的坑。无论你是期末复习、考研刷题还是工作后想补一补系统底层知识都可以照着这个思路往下读。1. 为什么I/O控制方式能决定整机性能——先搞清楚问题本质1.1 CPU与外部设备之间隔着一条“速度天堑”先做一个简单的算术题。现代CPU主频动辄3GHz以上执行一条简单指令大约只要0.3纳秒而一块机械硬盘寻道需要几毫秒哪怕是最快的NVMe固态硬盘一次访问也有几十微秒的延迟。也就是说CPU干完一件小事的耗时足够设备完成上万次“准备动作”。我经常用一个类比帮助学生理解CPU像一个百米能跑进9秒的运动员设备则像在几公里外的仓库工作一次“设备就绪”的消息要等上好几分钟才能送达。如果CPU非要站在原地等消息那它在这几分钟里什么都干不了。这还只是单个速度差操作系统里同时挂着磁盘、网卡、键盘、显示器每一个都可能随时让CPU进入等待状态效率损耗是灾难级的。所以I/O控制方式的核心问题只有一个当CPU发起一次与外设的数据交换后这个交换过程里CPU到底要参与多少参与得越多系统整体可用算力就越低参与得越少对硬件设计的要求就越高。1.2 I/O涉及的硬件面孔端口、寄存器与MMIO讨论控制方式之前先得说清楚CPU到底在跟谁打交道。CPU不是直接连着硬盘盘片或者网线接口的它面对的是设备控制器Device Controller比如磁盘控制器、网卡控制器。每个控制器内部有几种寄存器数据寄存器保存从设备读来或准备写给设备的数据状态寄存器反映设备当前是忙、是闲、是出错、数据是否就绪控制寄存器接收CPU下达的命令比如“开始读”“开始写”。CPU访问这些寄存器有两种路径。一种是用独立I/O地址空间典型就是x86上的IN/OUT指令通过端口号访问设备另一种是内存映射I/O把这些寄存器映射到内存地址空间CPU直接读写特定内存地址就行。现在很多高性能设备两种方式都会用寄存器和DMA缓冲区可能分布在不同的地址空间里。为什么要强调这些寄存器因为后面四种控制方式讲到底都是在描述同一个问题的不同解法数据如何在这些寄存器与内存之间移动以及这个移动过程需要CPU“亲自动手”几次。1.3 四条演进路线一条主线这四种方式不是四个平行无关的概念它们是一条清晰的演进链程序直接控制CPU死等设备状态并且自己搬运每一个数据中断驱动I/O设备完成操作后主动通知CPUCPU等待期间可以做其他事但数据仍然要CPU来搬DMA方式CPU把事情交代给DMA控制器数据搬运完全交给硬件CPU只在开始和结束时露面通道控制方式更进一步通道处理器自己能读懂一套I/O指令可以自动编排和完成一整批复杂的I/O操作。抓住“CPU参与程度逐渐降低”这条主线整章内容就有了骨架。接下来我们逐一拆开看。2. 程序直接控制方式CPU“死等”的老古董从哪来、还剩哪些用处2.1 最基本的轮询流程程序直接控制方式也叫程序查询方式是最朴素的手段。以CPU从外部设备读取一个字为例完整流程是这样的CPU把“读”命令写入设备的控制寄存器CPU反复读取状态寄存器检查其中的“就绪”位是否被置为1设备控制器执行命令把数据准备好后将数据放入数据寄存器并把状态寄存器置为“就绪”CPU查询到“就绪”后从数据寄存器取走数据数据被放到内存指定区域如果需要继续读就再发起下一轮操作。整个过程里CPU一直在一个循环里转圈查状态位。这就是所谓的忙等待busy waiting或轮询polling。2.2 为什么“忙等待”会白白烧掉CPU很多人初学时有个误解以为“查询”本身也是在工作。实际上查询循环只是在重复读一个寄存器、比较一个状态位并没有产生任何业务价值。设备准备数据要1毫秒CPU在这1毫秒里可能已经转了十几万次轮询干的全是复读机一样的活。我当年在公司调固件时遇到过更尴尬的情况设备已经在中断处理里把数据准备好了但由于驱动里的一个查询分支没有跳出等待CPU硬生生多空转了几百微秒才去取数。排查时用性能分析工具一看热点函数就是一个无比简单的状态位读取那种“CPU全力奔跑却毫无产出”的画面比教科书上任何一句话都真实。2.3 用数字算一笔账假设CPU主频2GHz一次轮询循环包括读寄存器、比较、跳转大约10个时钟周期也就是5纳秒左右。设备从接收命令到“就绪”需要1毫秒。那CPU至少要查询1毫秒 ÷ 5纳秒 20万次20万次轮询里真正有用的只有最后一次——发现数据就绪并读走。其余199999次都是无用功。如果系统里同时有多个设备在用程序直接控制方式CPU利用率会迅速被拖垮这也是它无法支撑多道程序系统的主要原因。2.4 这方式到今天还有用吗有而且比很多人想象的用得还多。最典型的就是系统启动阶段BIOS/UEFI引导时中断向量表、DMA控制器可能还没初始化好驱动也没完全加载直接用轮询方式访问IDE/SATA控制器是最稳的。另一个典型场景是嵌入式单片机上的短等待操作比如触摸屏控制器、ADC采样等待时间极短且确定配置一套中断反而小题大做。还有一些设备出错后驱动会主动退回轮询因为它简单、可靠、问题可控。所以我不太建议把“程序直接控制”简单理解为“淘汰的老技术”它更像是保留在工具箱底部的扳手不常用但关键时刻很可靠。3. 中断驱动I/O方式把“死等”改成“等了再叫”但搬运还得动手3.1 从轮询到中断思路的历史性转变程序直接控制方式最大的问题是CPU在等待设备时不能转身去做别的事。中断驱动I/O就是针对这一点改进的设备操作完成后主动通过中断请求通知CPUCPU收到通知后再去处理数据。打个比方轮询方式等于每隔几秒就到楼下看一次快递柜中断方式给快递员发了一把门铃他到了按门铃你听见了再下楼取。对CPU来说“下楼”这件事没有消失但等待期间它可以在房间里做其他重要工作。中断方式的基本流程如下CPU发起I/O命令后不再进入轮询循环而是保存好当前进程的上下文转去调度其他任务设备控制器和硬件协作完成数据的准备设备控制器向中断控制器发出中断请求信号中断控制器根据优先级和屏蔽规则把请求递交给CPUCPU在当前指令边界响应中断保存现场CPU从中断向量表找到对应的中断处理程序开始执行中断处理程序检查设备状态把数据寄存器里的值读入内存或内核缓冲区并唤醒等待这个I/O的进程恢复之前被中断的现场。3.2 硬件协作中断控制器和中断响应链中断不是设备直接连一根线到CPU就完事。现代系统里一般有中断控制器比如x86的APIC它负责收集多个设备的中断信号进行优先级仲裁再向CPU提交中断请求。CPU最终要跳转到哪个处理函数靠的是中断向量号查中断向量表。这里有一个值得注意的细节CPU响应中断后自己并不知道是谁发出的中断它要先读中断控制器的中断号寄存器再去查表跳转。这个过程在很多人看来只是“查个表”实际在硬件层面有好几级时序配合任何一个环节被屏蔽或误配置都会表现为设备罢工或CPU被中断风暴淹没。3.3 中断方式的收益与隐藏成本收益是显而易见的设备准备数据期间CPU能去执行其他进程宏观上CPU利用率大幅提升。但中断不是免费的它隐藏了几笔成本第一每次中断都要保存和恢复现场涉及通用寄存器、程序计数器、状态字等可能要耗费几百甚至上千个时钟周期。第二中断处理程序是在受限上下文里执行的不能做太重的逻辑更不能睡眠否则会把系统拖住。第三最关键的一点中断方式只是把“等待”去掉了数据搬运本身仍然靠CPU执行指令完成。设备每准备好一个字节或一个字就要产生一次中断CPU在中断处理程序里逐字搬数据。3.4 实例低速设备、串口与Linux的NAPI低速设备用中断毫无压力比如键盘。人敲键盘一秒最多几十次每次一个扫描码中断一次CPU完全轻松。老式串口设备在波特率不高时每个字符中断一次也可以接受但速率一旦上去CPU就会明显吃力。真正把这个问题暴露出来的场景是高速网卡。如果每个网络包都产生一次中断CPU可能修了中断之后连协议栈处理的余力都没有。Linux的NAPI机制就是针对这种局面的折中方案低负载时用中断唤醒CPU处理数据高负载时切换成轮询模式主动批量从网卡队列取包用轮询规避中断风暴。所以“中断一定比轮询高效”这个说法是错误的不同负载下各有优势。4. DMA方式数据搬运的转折点4.1 DMA控制器接管搬运中断驱动虽然解放了CPU的等待时间但没解放CPU的搬运劳动。试想磁盘一次读入512字节扇区如果每字都中断一次处理器就要被打断几百次每次还要保存恢复现场开销依然吓人。于是硬件工程师想到一个办法做一个专门的控制器让它代替CPU完成设备与内存之间的数据搬运这就是DMA控制器简称DMAC。可以这么理解中断方式是“你在家听见门铃响然后自己下楼取快递”DMA方式是“你在家办公快递放在驿站后驿站经理安排专人把包裹直接送到你房间门口再按一下门铃告诉你货到”。这里的“驿站经理”就是DMA控制器“按门铃”就是DMA传输完成后发出的中断。4.2 一次DMA读操作的完整流程DMA不是完全不经过CPU而是把CPU的参与压缩到“开头交代”和“结尾确认”两步CPU初始化DMA控制器写入数据源地址比如设备的数据寄存器地址、目标内存地址、传输长度、传输方向等CPU向设备控制器发出“启动传输”的命令设备每准备好一个字节或一个字就向DMAC发出DMA请求DMAC通过总线仲裁获得总线使用权在设备数据寄存器和内存之间完成一次直接传输重复第3、4步直到传输计数归零DMAC向CPU发出中断通知“整个数据块已经传输完毕”CPU的中断处理程序随后对缓冲区做业务处理。注意第6步DMA传输完成依然会产生中断。很多人会误记成“DMA不用中断”这是不对的。准确说法是DMA把中断频率从“每个数据单位一次”降到“每批数据一次”。4.3 三种总线使用模式块传送、周期挪用、透明DMADMAC要搬数据就得用系统总线但总线同时也是CPU访问内存的必经之路。DMAC怎么拿总线直接决定它对CPU性能的影响程度常见有三种模式块传送模式DMAC一旦获得总线控制权就持续占用直到整个数据块传完。这种方式吞吐率最高但传输期间CPU没法访问内存可能被完全阻塞符合“快但粗暴”的特点。周期挪用模式DMAC只在CPU不访问内存的总线周期里插入传输做完一个小周期就释放总线。这种方式对CPU影响最小但整个传输时间会被拉长适合数据量不大或对CPU响应很敏感的场景。透明DMA模式DMAC持续监听总线只在CPU完全不需要总线时传送数据。它最节省系统资源但实现起来最复杂而且可能等很久才能等到足够的空闲周期。我调试设备驱动时遇到过厂商默认把DMA设成块传送模式、结果一跑大数据量任务系统调度就卡顿的案例。后来改成周期挪用CPU侧时延立刻正常代价是I/O吞吐略微下降。这说明没有绝对最好的模式只有和业务负载匹配的模式。4.4 DMA与中断方式的关键区别DMA和中断驱动最核心的区别不是“有没有中断”而是“数据由谁搬运”。中断驱动方式下中断处理程序里还得有一条条取数指令DMA方式下数据是DMAC借总线直接搬运的CPU的中断处理程序只需要做缓冲区管理和业务通知。另外还有一个容易忽略的点DMA并不是所有情况下都比程序直接控制更快。传输几个字节的小数据初始化DMAC、做总线仲裁的开销可能比CPU自己搬还大。所以系统设计中有时候会在传输粒度很小的情况下主动放弃DMA选择PIOProgrammed I/O直接读写寄存器这也是一种经验。5. 通道控制方式真正意义上的I/O处理器5.1 通道是什么如果把DMA控制器比作一个“专职搬运工”那通道就是“搬家公司经理”它有自己的指令系统能够读取存放在内存里的通道程序一条条解释并执行它能同时管理多台设备它可以根据条件自动完成一系列I/O操作而不只是单纯地搬连续数据块。通道本质上是一个专门处理I/O的处理器有的资料干脆叫它I/O处理器I/O Processor。它解决的是DMA还没解决的最后一公里问题DMA一次只能按预先设定的单一方向搬一段数据如果需要分多次、多方向、多设备协调操作CPU就还得频繁介入。通道把这件事进一步自动化了。5.2 通道程序CPU只下达一条启动指令通道工作的核心是通道程序Channel Program。通道程序由若干条通道指令CCW组成每条通道指令大致包含操作码读、写、控制等、内存缓冲区地址、传送字节数、标志位和指向下一条指令的指针。一次典型的通道I/O过程是这样的CPU在内存中构造好一个I/O控制块和对应的通道程序CPU通过一条“启动I/O”指令把通道程序的首地址告诉通道通道接管后续工作从内存逐条读取CCW、解析、执行真正搬运数据时通道会利用DMA机制在设备和内存之间直接传数整个通道程序执行完毕或者中途出错通道向CPU发送中断CPU做收尾确认。可以看到CPU在整批I/O过程中只露了两次面发起时给一条启动指令结束时响应一次中断。中间所有的指令解析、缓冲管理、设备调度、数据搬运都是通道自己完成的。5.3 三种通道类型选择通道、字节多路通道、数组多路通道通道按数据传送方式和可同时服务的设备数分成三种经典类型考试里出对比题的概率很高。选择通道一次只为一台设备服务整块数据连续传送适合磁盘这类追求高吞吐的设备。优点是速度最快缺点是通道利用率低——设备在寻道或转动时通道闲着。字节多路通道以字节为单位分时交叉为多台慢速设备服务。终端、打印机这类字符设备很适合通道利用率很高但单字节速率低。数组多路通道以数据块为单位在多台中高速设备之间交叉传送。它既能同时管理多台设备又能提供较宽的数据通路算是一种折中和主流方案。三者比较可以用下面这张表概括通道类型数据传送单位同时服务设备数适用设备特点选择通道整个数据块1台高速块设备如磁盘吞吐率最高设备切换时利用率低字节多路通道字节多台按字节交叉慢速字符设备并行度高单路速率低数组多路通道数据块多台按块交叉中高速块设备兼顾并行度与速率5.4 通道控制与DMA的本质差异面试时经常有人把通道和DMA混为一谈其实核心差异很清楚DMA控制器是“硬逻辑”设备基本只能按CPU设定好的参数机械搬数据通道是有指令处理能力的“可编程处理器”能执行通道程序能判断操作码能在多台设备之间切换。通道一般都内置了DMA能力但DMA不等于通道。还有一点值得注意通道控制的场景在大中型机和高端存储系统中更常见普通PC上的独立磁盘控制器、NVMe控制器其实也具备很多类似通道的可编程处理能力。现代设备越来越聪明CPU越来越“少管闲事”这个趋势从程序直接控制一路走到通道控制至今没有变过。6. 四种方式横向对比以及考场和面试里最常见的坑6.1 一张表看懂四种方式控制方式CPU干预程度数据传送单位主要硬件支持CPU与I/O设备并行程度典型场景程序直接控制每个数据单位都要查询等待并搬运字节/字I/O控制器、寄存器基本不并行CPU全程等待启动引导、简单嵌入式控制中断驱动I/O每个数据单位一次中断中断里搬数据字节/字中断控制器等待期间可运行其他程序数据搬运仍需CPU键盘、低速串口、部分网络设备DMA方式每批数据开始设置和结束中断各一次数据块DMA控制器、总线仲裁CPU与DMAC并行数据搬运由硬件完成磁盘、SSD、网卡等块设备通道控制每条通道程序启动和结束中断各一次数据块/整批数据I/O通道处理器CPU高度并行通道可自动执行多步I/O大中型机、高性能存储阵列这张表建议直接背下来因为大部分题不管怎么变最后都会落到“谁在搬数据”和“CPU多久醒一次”这两个问题上。6.2 现代操作系统里其实是“混搭”你去看一台跑着Linux的普通电脑会发现四种方式实际上并存系统启动用轮询键盘走中断磁盘I/O走DMA网卡在高负载时从NAPI中断模式切到轮询模式一些企业级存储控制器自带通道级的I/O处理能力。这说明选择控制方式没有一个“最优”而是看数据速率、时延要求和CPU资源的余量。我在做性能分析时排查CPU占用异常的第一步就是看中断频率和设备队列状态因为I/O控制方式的任何不合理配置最终都会以“CPU被打断次数太多”或“CPU空转等待”这两种现象之一暴露出来。6.3 最容易错的几个点第一程序直接控制与中断驱动的区别不在于CPU要不要参与数据搬运而在于等待期间CPU能不能干别的。第二DMA与中断驱动的本质区别是数据搬运交给了谁不是“有没有中断”。第三通道与DMA的本质区别是是否有指令系统、能否执行通道程序而不是“谁更快”。第四DMA的周期挪用模式并不完全阻塞总线透明DMA甚至只在CPU不用总线时传输所以“DMA一定拖慢CPU”这种话是片面的。还有一个高频易错题通道程序放在哪答案是内存。CPU把通道程序的地址交给通道通道自己去内存取指令执行。很多人误以为通道程序存在设备控制器里这就不对了。6.4 复习建议画时序图比背定义有效我的个人经验是复习这部分别死记定义手动画一张CPU活动时序图。横轴是时间纵轴分别标出CPU、DMAC、设备的忙闲状态然后把一次磁盘读请求从系统调用到中断返回的过程逐段标出来。只要你能画出中断方式里“等待期间CPU在跑别的进程”、DMA方式里“CPU在搬运期间完全闲着”你对本章的理解就大概率过关了。对准备考研的同学多说一句这个知识点在选择题、简答题、综合题里都能出且常和“系统调用”“中断处理”“缓冲区管理”结合起来考。按上述主线去理解遇到什么变化都不慌。最后分享一个我在实际项目里的体会有一次处理高负载下的CPU占用率飙升问题排查了很久才发现是网卡中断太过密集把CPU打断到几乎没法执行业务代码。那一刻我特别庆幸自己当年认真画过那几张时序图——很多东西在教科书里只是考点到了生产环境就是一把能帮你快速定位故障的钥匙。搞清楚这四种控制方式怎么走学到的不是一道题的答案而是一种看系统的新眼光。
返回列表