【问题标题】:Polling or Interrupt based method基于轮询或中断的方法
【发布时间】:2011-03-05 14:36:04
【问题描述】:

什么时候应该使用轮询方法,什么时候应该使用基于中断的方法? 有没有可以同时使用的场景?

【问题讨论】:

    标签: embedded operating-system interrupt processor rtos


    【解决方案1】:

    如果感兴趣的事件是:

    1. 异步
    2. 紧急
    3. 不常见

    那么基于中断的处理程序就有意义了。

    如果感兴趣的事件是:

    1. 同步(即您知道何时在一个小窗口内期待它)
    2. 不紧急(即缓慢的轮询间隔没有不良影响)
    3. 频繁(即您的大多数轮询周期都会产生“命中”)

    那么轮询可能更合适。

    其他考虑因素包括您是为操作系统编写设备驱动程序,还是仅编写不支持线程的裸机代码。在裸机情况下,CPU 通常只是在不忙的时候循环,所以它还不如轮询一些东西。

    【讨论】:

    • 如果事件频繁发生,为什么不首选基于中断的方法?
    • 这不是我写的。如果它不频繁,那么轮询它会浪费大量 CPU。如果它很频繁,那么根据其他因素,任何一个都可能是合适的。
    • 如果它非常频繁,您可能必须使用 ISR 来确保您得到它,但是您需要缓冲它并将其交给后台循环或任务。
    • 一个中断比一个轮询周期有更多的开销,所以如果事件是频繁的轮询会产生更少的开销。
    • 是不是在多核处理器中,如果应用程序在Linux上,那么它必须是基于中断的数据包处理。但是,如果它是在纯数据路径中,那么可以使用轮询,因为它似乎提供了更高的性能?
    【解决方案2】:

    应尽可能避免轮询,因为它通常会不必要地占用大量 CPU 周期(除非 (a) 您只打算轮询很短的时间,或者 (b) 您可以负担得起合理的睡眠时间)你的轮询循环)。浪费 CPU 周期不仅从性能角度来看是不好的,而且还会增加功耗,这很可能是电池供电的嵌入式应用程序的一个问题。

    【讨论】:

      【解决方案3】:

      在决定轮询或中断时,您必须充分了解您尝试关注的事件的性质以及您对它的反应。

      当什么都没有发生时,中断不需要处理,但当有事情发生时,需要你全神贯注。如果事件是外部事件并且具有嘈杂的边缘或快速脉冲,那么这可能会导致中断的严重问题,您必须小心设置中断。

      在这个例子中,中断例程响应激光束变得清晰,并为它被阻塞的事件设置自己:

         BEAM_INTR_EN = TRUE;   /*re-enable the beam interrupts*/
      
         /*Set the beam interrupt for the next clear to blocked event*/
         BEAM_INTR_EDGE = CLEAR_TO_BLOCKED;
         BEAM_INTR_FLAG = FALSE; /*Clear the interrupt*/
      

      这段代码有两个弱点: 1) 如果在清除中断标志之前激光束再次被阻挡(BEAM_INTR_FLAG = FALSE;)。中断将被错过,代码将与激光束状态不同步。

      2) 在后台程序中设置中断或为高于此代码所在优先级的优先级设置中断时,启用中断时必须小心。如果中断标志在启用之前已经设置(错误地),则中断例程将在启用后立即被错误地调用,并且可能是错误的边沿。

      修复 1) 的最简单方法是在设置中断后仔细检查,如果已发生则强制中断。修复 2) 将中断的启用移到仔细检查之后:

         /*Set the beam interrupt for the next clear to blocked event*/
         BEAM_INTR_EDGE = CLEAR_TO_BLOCKED;
         BEAM_INTR_FLAG = FALSE; /*Clear the interrupt*/
      
         /*Double check beam state to see if it has already gone blocked*/
         if (BEAM_STATE == BEAM_BLOCKED)
         {
            BEAM_INTR_FLAG = TRUE; /*Force the interrupt to re-enter the ISR after exiting*/
         }
         BEAM_INTR_EN = TRUE;    /*re-enable the beam interrupts*/
      

      强制中断使系统工作在同一个状态机上,只是手动强制循环以覆盖盲点。

      基本上:

         Set the edge to detect the next interrupt event
         Clear the interrupt flag
         if (the event has already occurred)
         {
            Set the interrupt flag to force the interrupt
         }
         Enable the interrupt
      

      如果对事件的响应时间必须一致(例如,输入线变高后 1ms +/-10us,传输事件信号),那么中断通常是最好的。

      如果对事件的响应时间必须在一定时间内(例如,在输入线变为高电平的 1ms 内,传输事件信号),那么最好使用中断。

      中断的问题是您必须开始考虑线程,并且两段代码可以同时访问相同的数据。

      中断还有助于处理器在等待某事发生时进入低功耗模式(睡眠/空闲等)。

      话虽如此,如果处理器只有一件事要做,轮询可以对事件做出非常紧张的时间响应,但通常中断硬件需要几个周期来响应事件,而紧张的轮询循环可以做到。

      如果事件不是时间关键且可能有噪音(例如有人按下开关),则轮询允许简单过滤而不会错过长期转换。一个常见的错误是在设置时多次轮询:

      void fnInitialiseSystem(void)
      {
         if (MODE_INPUT == MODE_A) /*First polling of the MODE_INPUT*/
         {
            PR2 = PR2_MODE_A;
         }
         else
         {  
            PR2 = PR2_MODE_B;
         }
         OpenTimer2( TIMER_INT_ON &
                     T2_PS_1_1     &
                     T2_POST_1_8   );
      
         if (MODE_INPUT == MODE_A) /*Second polling of the MODE_INPUT*/
         {
            CurrentMode = MODE_A;
            PROBE_INT_EDGE = CLEAR_TO_BLOCKED;
         }
         else
         {  
            CurrentMode = MODE_B;
            PROBE_INT_EDGE = BLOCKED_TO_CLEAR;
         }
      }
      

      在上面的例子中 MODE_INPUT 是一个外部开关,如果两次 MODE_INPUT 被轮询的时间不同,那么行为是意外的。在读取这些类型的信号时,最好使用过滤来决定输入的长期状态,并对过滤后的版本执行操作。

      例如,使用开关去弹跳只需定期检查一个开关(每 1 毫秒?),如果其中一些(比如 16 个)与过滤版本(开关打开)不同(开关关闭),则更新结果并执行所需的行动。小心信号混叠,振荡信号可能看起来很稳定!

      再次使用轮询和中断的示例是使用不经常更改但在更改时会产生噪声的输入。开关也是一个很好的例子:代码可以设置一个中断来检查开关状态的变化,当中断发生时,可以定期轮询开关,直到开关状态“稳定”(要么改变状态或回到原来的状态)。这样做的好处是在什么都没有发生时处理开销低,而在有事情发生时可以过滤噪声。

      【讨论】:

        【解决方案4】:

        有时您实际上需要同时使用两者。例如,如果事件是零星的,但以高速爆发的形式出现;您可能需要首先响应中断,然后在重新启用中断轮询之前查看是否已经发生了另一个事件,以避免中断上下文切换的一些开销。我相信 Linux 网络接口在这种模式下运行。

        【讨论】:

        • 我们的嵌入式 12Mbaud 通信驱动程序使用这样的方法 - 当字符到达时中断,然后在退出前轮询以从小型 fifo 中获取尽可能多的字符。
        • @Simon:你能澄清一下你所指的linux内核版本吗? linux kernel 3.16的网络接口是否也一样?
        【解决方案5】:
        【解决方案6】:

        简短的回答是在轮询太慢时使用中断方法。 (太慢,我的意思是如果轮询丢失数据,中断方法是必要的)

        【讨论】:

          【解决方案7】:

          基本上,如果由于某些硬件或软件原因中断模式不可用,则使用轮询模式。因此,从功耗、性能等角度来看,中断模式更可取(同意 Paul R)。轮询模式也可用于原型设计、无需外围设备的内核以及用于某些测试目的。

          【讨论】:

          • 我想补充一下,例如,中断模式提供了更好的组织软件(但这不是一个规则)。
          【解决方案8】:

          当需要低延迟时,首选中断。如果您每秒轮询某个条件 N 次,那么平均而言,您会在实际发生后 1/N 的一半时间内发现该条件。

          当需要绝对确定的时序时,有时首选轮询。就其本质而言,中断可能发生在不可预测的时间,并使时序分析变得非常复杂,而对于轮询系统,就截止期限的满足做出可证明的陈述相对容易。

          【讨论】:

            【解决方案9】:

            始终使用中断。这样您就永远不会丢失数据。在事件驱动或线程应用程序中,即使是最慢的信号也应该是中断驱动的。

            您应该使用轮询的唯一时间是当您使用调度程序并且硬件上的缓冲区足够深以确保不会丢失数据时。

            【讨论】:

              【解决方案10】:

              轮询模式在具有高频事件的系统中很有用,在这些系统中,与进入和退出中断处理程序相关的开销比简单的轮询使用更多的 CPU 周期。例如,可以在 IP 路由器中使用轮询来最大化可用于数据包处理的 CPU 带宽。

              【讨论】:

                【解决方案11】:

                有许多设计约束可以推动决策。我的应用结合了中断和轮询:

                • 外部和内部时钟源触发中断 - 这对 时间戳都准确,所以我们可以同步它们。
                • 传入的串行消息触发中断。接收 FIFO 必须在溢出之前得到服务。
                • 当 FIFO 部分为空时,传出消息会触发中断 - 必须在下溢之前重新填充。
                • 在后台轮询的 ISR 设置的信号量。这有两个优点:
                  • 处理传入事件所需的计算可能很长;如果将其留在 ISR 中,则可能会延迟其他 ISR 超出其服务期限。
                  • 可以对事件进行排序。例如,轮询循环可以确保计算 X 始终发生在 ADC 数据收集和传入消息解析之间,即使有时消息比预期早一点到达。

                【讨论】:

                  【解决方案12】:

                  您不希望主机在繁忙的循环中等待很长时间,而且当频繁检查不经常存在的数据时,轮询可能会变得低效。因此,如果主机和设备都很快,那么轮询是否很快。

                  【讨论】:

                    【解决方案13】:

                    polling based 相比,使用Interrupt based design 要好得多,因为基于轮询的方式存在缺陷,因为它希望在每次轮询时返回数据。现在,您可能会说我会解决这种情况,即单个轮询返回了一个错误,但是为什么要浪费所有的 CPU 周期来轮询某些东西,而它也可以返回一个错误呢?并且预期投票可能会失败是实际的产品场景。

                    Interrupt based designs 在单个轮询中涉及很多层功能时更有意义。对我来说,这是一种常见的做法:你会每天一遍又一遍地问(投票)你的朋友他是否有你需要的信息,或者你会在你有信息的时候告诉他interrupt我吗我需要。我认为我们在日常生活中做了正确的事,但没有意识到。

                    但是interrupt based architectures 在实施时需要对publish-subscribe design principle 有深入的了解。而且,当在应用程序域中完成时,它们要求发送中断的部分代码写得非常好。这很好,因为它也将复杂性压缩到一个地方。

                    除上述之外,以下是基于轮询的架构免费为您提供的其他优势:

                    • 异步
                    • 非常适合不经常发生的事件/更新
                    • 仅在有数据可用场景时更新
                    • 更好的错误处理和管理
                    • 更好地利用 CPU 周期
                    • 更好的电池寿命管理
                    • 让听众远离底层的复杂性

                    当您设计sw 并且您有此选择时,您应该始终选择基于interrupt 的设计而不是基于polling 的设计,因为基于interrupt 的设计可以使用侦听器填充基于polling 的情况但是基于轮询的设计永远无法满足需要基于interrupt 的设计的要求。

                    下面是一个简短的比较矩阵:

                                         -INTERRUPT-      -LOOP-
                    Speed                  fast            slow
                    Eficiency              good            poor
                    CPU waste              low             high
                    multitasking           yes             no
                    complexity             high            low
                    debugging              +/- easy        easy
                    critical in time       excellent       poor
                    code bloat             low impact      high impact
                    

                    【讨论】:

                      【解决方案14】:

                      看,我们主要有 5 种方法:

                      1) 盲人

                      CPU 每 x 毫秒检查一次数据。 ETC 检查引脚 12。

                      2)轮询(忙/等待)

                      CPU 一直在检查并等待 Flag 被提升,就像 UART 在数据包传输后提升一个标志一样。永远检查标志寄存器。 (最佳响应时间)但 CPU 无法执行任何其他操作。

                      3) 中断:

                      CPU 正常运行,如果发生中断,CPU 会将上下文切换到 ISR。如果引脚 18 出现下降沿,则执行 ISR (1)。不错的响应时间和 CPU 在 ISR 不活动时可以做任何事情。使用您不知道何时可能发生的紧急应用程序。

                      4) 定期轮询:

                      CPU 正在做它的事情,但每隔 ms 秒检查一次它的引脚 11。Blind 在两者之间什么都不做。更糟糕的响应时间,而不是紧急应用程序,当您不信任硬件时会引发中断。它可以使用定时器中断来创建。

                      5) 直接内存访问。

                      高级接口方法。直接从/向内存传输数据。 输入将直接读入内存。 输出将直接从内存中写入。 两者都使用控制器。

                      【讨论】:

                        猜你喜欢
                        • 2011-03-08
                        • 2014-06-28
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-06-15
                        • 2012-05-10
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多