常见轮询模型:忙等待、定时轮询、中断驱动轮询的优劣对比

做硬件调试这些年,轮询模型是绕不开的老朋友。你想想看,系统要对外设状态做合规性检查,到底让 CPU 怎么等?是死死盯着,还是干点别的再回来看?这里头门道不少。我个人习惯把三种模型画成一张图,调试时一眼就知道问题出在哪——先看这张框架图。

三种轮询模型核心特征对比 轮询模型核心特征框架 忙等待 CPU 始终占用 持续检查状态寄存器 响应延迟:极低 CPU 利用率:极低 定时轮询 固定时间间隔查询 CPU 可让出执行其他任务 响应延迟:取决于定时周期 CPU 利用率:中等 中断驱动 硬件事件触发 CPU 被动响应 响应延迟:极低(硬件级) CPU 利用率:极高 关键权衡:响应延迟 vs CPU 利用率 vs 实现复杂度 超时合规场景下,没有银弹,必须根据外设及时性要求选择

忙等待——最直接也最“蛮干”的方式

忙等待,说白了就是写个死循环读状态寄存器。CPU 啥也不干,就盯着那个 bit 翻转。

volatile uint32_t *status_reg = (uint32_t *)0x40001004;
while (!(*status_reg & STATUS_READY)) {
    // 空转,啥也不干
}
// 外设准备好,继续处理

这种模型优点很明显:响应快,没有调度抖动,代码简单得像白开水。我在项目中遇到过最典型的用例——芯片复位后的启动序列,PLL 锁定需要几十微秒,用忙等最合适,因为这时候系统还跑不了别的。

⚠️ 踩坑警告:我曾经在一个多任务系统里用了忙等,结果 CPU 一直占着,看门狗喂不上,系统反复重启。避开误区关键点:忙等只适合“短时间等待 + 单任务场景”。

缺点也是致命的——CPU 利用率几乎为零。你要是让 i.MX6 处理器在 800 MHz 下空转等待一个 10 ms 的外设,你想想看,800 万个时钟周期全浪费了。

定时轮询——折中的“定时查岗”

定时轮询的思路是:我每隔 N 毫秒派一个定时器中断,在中断里检查外设状态。主业干别的,副业查查岗。

void timer_isr(void) {
    if (*status_reg & STATUS_READY) {
        process_ready();
    }
    // 清除定时器中断标志
    *timer_clear = 1;
}

好处很清楚:CPU 在等待期间可以跑其他任务,适合中等实时性要求。我之前的相机对焦项目,传感器就绪时间在 5~50 ms 间波动,设 2 ms 的定时轮询,效果不错。

但有个坑:定时周期选不好,轻则响应慢,重则错过窗口。比如外设状态只保持 1 ms,你轮询间隔 5 ms,那就永远抓不到。

定时周期响应延迟CPU 开销典型场景
100 μs≈150 μs高(接近忙等)高精度外设
1 ms≈1.5 ms中常规外设
10 ms≈15 ms低慢速传感器
💡 实践技巧:我建议在选择定时周期间隔时,小于外设最坏状态保持时间的一半,同时保证 CPU 开销能接受。

中断驱动轮询——硬件帮忙“嗑一声”

中断驱动,说白了就是让硬件主动通知你。外设就绪了,拉一根中断线,CPU 在中断服务程序里处理。

void ext_int_isr(void) {
    uint32_t data = *status_reg; // 读取状态并清除中断
    process_ready(data);
}

这是三种模型里 CPU 利用率最高的——平时 CPU 该干嘛干嘛,只有事件来了才介入。响应也最快,硬件触发到进入 ISR 通常只要几十纳秒。

但别高兴太早。中断驱动最大的坑是:中断风暴!我记得在调试一个 SPI Flash 擦写流程时,外设每完成一个 byte 就触发一次中断,频率高达 5 MHz,CPU 被活活拖死在中断上下文切换里。另一个问题是优先级和嵌套,搞不好就死锁了。

🔑 精华总结:中断驱动适用于“事件不确定且频繁度可控”的场景。如果外设事件速率 > 20 kHz,建议用硬件 FIFO + DMA 来降低中断频率,不然 CPU 光进出中断就废了。

三种模型对比——一张表讲清楚

特性忙等待定时轮询中断驱动
响应延迟极低(几周期)取决于定时周期极低(纳秒级)
CPU 利用率极低(几乎为 0)中等极高(事件触发才占)
代码复杂度极低中等(需定时器)高(ISR/优先级/群)
可预测性高(确定性)中等(受调度影响)低(中断延迟抖动)
CPU 空闲时可处理其他任务否是是
典型失败案例看门狗溢出错过数据窗口中断风暴导致死锁

我的选型经验——没有银弹

做 Polling Compliance 超时调试时,我个人习惯先问三个问题:

  1. 我需要多快的响应?——微秒级以下用忙等或中断;毫秒级可用定时轮询。
  2. CPU 还能不能干别的?——如果不能,忙等就是最省事的方案;如果能,优先考虑中断或定时轮询。
  3. 外设事件频率稳定吗?——稳定且不高,定时轮询很好;突发且高时,中断必须配去抖。
⚠️ 别忘了超时合规:无论选哪种模型,都要设计超时保护。我曾经在一个产品里只用了忙等没加超时计数器,结果外设故障挂死,整机死机。从那以后,我的忙等循环里都会加一个超时跑出机制。