假设有一个微处理器或微控制器,当它注意到按钮被按下时,它应该执行一些操作。
第一种方法是让程序进入一个循环,该循环除了查看按钮是否已更改外,什么都不做,一旦更改,就执行所需的操作。
在某些情况下,第二种方法是对硬件进行编程以在按下按钮时触发中断,假设按钮已连接到已连接的输入,因此它可能会导致中断。
第三种方法是配置一个计时器以某种速率(例如,1000x/秒)中断处理器,并让该中断的处理程序检查按钮的状态并对其采取行动。
第一种方法使用忙等待。它可以为一种特定的刺激提供非常好的响应时间,但代价是完全忽略其他一切。第二种方法使用事件触发中断。它通常会提供比忙等待稍慢的响应时间,但允许 CPU 在等待 I/O 时做其他事情。它还可能允许 CPU 进入低功耗睡眠模式,直到按下按钮。第三种方法提供的响应时间远低于其他两种方法,但即使硬件不允许按钮按下触发中断,也可以使用。
在需要快速响应的情况下,通常需要使用事件触发中断或忙等待。然而,在许多情况下,轮询方法可能是最实用的。可能不存在支持人们可能感兴趣的所有事件的硬件,或者人们感兴趣的事件的数量可能大大超过可用中断的数量。此外,某些条件可能需要生成延迟响应。例如,假设一个人希望计算开关被激活的次数,符合以下标准:
- 每个合法的开关事件都包含一个从 0 到 900us(微秒)的时间间隔,在此期间开关可以任意关闭和重新打开,然后是至少 1.1ms 的时间间隔,在此期间开关将保持关闭状态,然后是从 0 到900us,在此期间开关可以任意分闸和重合闸,随后有至少1.1ms的间隔,在此期间开关将分闸。
- 在任何未忽略的开关打开或关闭后,软件必须忽略开关的状态达 950us。
- 允许软件任意计数或忽略在上述要求的消隐间隔之外发生但持续时间小于 1.1ms 的开关事件。
- 软件上报的计数必须在开关稳定“闭合”后的 1.99ms 内有效。
执行此要求的最简单方法是以 1,000x/秒的速度观察交换机的状态;如果在前一个状态为“打开”时看到“关闭”,则增加计数器。非常简单易行;即使开关以各种奇怪的方式打开和关闭,在真实事件之前和之后的 900us 期间,软件也不会在意。
可以使用开关输入触发的中断和定时器来对开关输入产生更快的响应,同时满足所需的消隐要求。最初,输入将在下次开关闭合时触发。一旦中断被触发,软件会禁用它,但会设置一个定时器以在 950us 后触发中断。一旦该计时器到期,它将触发一个中断,该中断将在下次开关“打开”时触发中断。该中断将依次禁用开关中断并再次将定时器设置为 950us,因此定时器中断将再次重新启用开关中断。有时这种方法很有用,但软件比简单的轮询方法复杂得多。当基于计时器的方法足够时,
在使用多任务操作系统而不是直接中断的系统中,许多相同的原则都适用。与在某些事件发生之前操作系统不会运行的代码相比,定期 I/O 轮询会浪费一些 CPU 时间,但在许多情况下,事件响应时间和没有事件发生时浪费的时间量在使用时都是可以接受的定期轮询。实际上,在某些缓冲 I/O 情况下,定期轮询可能会非常有效。例如,假设一个人正在通过串口从远程机器接收大量数据,每秒最多会到达 11,520 个字节,设备将在最后一个确认的数据包之前发送最多 2K 的数据,并且串口有4K 输入缓冲区。虽然可以使用“数据接收”事件处理数据,简单地检查端口 100x/秒并处理到目前为止收到的所有数据包可能同样有效。当远程设备不发送数据时,这种轮询会浪费时间,但如果预期传入数据,则以大约 1.15K 的块处理它可能比处理每一小块传入数据更有效它进来了。