硬件定时器如何工作并影响软件性能?

sli*_*kin 0 c++ assembly asynchronous timer lock-free

我想使用异步函数调用。我选择了boost::deadline_timer

对我来说,硬件计时器是一种特定的硬件(令人惊讶),它独立于 CPU 工作,仅用于监视时间。同时,如果我理解正确,它也可以用于设置超时并在达到超时时产生中断。(计时器

其主要优点是异步执行。设置定时器的线程可以继续工作,回调函数将在设置定时器的同一线程中触发。

让我描述一下我在行动中看到的情况。

  1. 应用程序包含一个或多个工作线程。例如,他们处理输入项目并过滤它们。让我们考虑应用程序有 5 个线程,每个线程设置一个计时器(5 秒)。

  2. 应用程序正在运行。例如当前线程是thread-3.

  3. 计时器 ( thread-0) 已到期并生成(可能是错误的术语)中断。

  4. 线程上下文切换 ( thread-3-> thread-0);

  5. 回调函数执行;

  6. 计时器 ( thread-1) 已到期并产生中断。

...

等等

PS0。我知道这不仅仅是多线程应用程序的一种可能情况。

问题:

  1. 我是否正确描述了工作过程?

  2. 难道我理解正确的话,即使当前线程thread-0它也导致上下文切换,因为线程必须停止执行当前的代码,并切换到执行从回调机能的研究码?

  3. 如果每个线程设置 100k 或 500k 计时器,它将如何影响性能?

  4. 硬件是否有计时器计数的限制?

  5. 更新计时器的超时时间有多昂贵?

Mar*_*oom 6

硬件定时器的核心只是一个递增计数器和一组比较器(或一个递减计数器,它使用 MSb 的借位作为与 0 的隐式比较)。
把它想象成一个寄存器,它有一个专门的操作Increment(或Decrement),它在一个时钟的每个周期开始(最简单的计数器类型是Ripple-counter)。
每个周期计数器值也被馈送到比较器,之前加载了一个值,其输出将是 CPU 的输入(作为中断或专用引脚)。
在递减计数器的情况下,从 MSb 借位作为值翻转为零的信号。
这些定时器通常具有更多功能,例如能够在达到所需值后停止(单次)、重置(周期性)、交替输出状态低和高(方波发生器),以及其他奇特的功能。

一个包装上可以放多少个定时器是没有限制的,当然,虽然电路简单,但在金钱和空间方面还是有成本的。
大多数 MCU 有一个或两个定时器,当有两个时,想法是使用一个用于通用调度,另一个用于与 OS 调度正交的高优先级任务。
值得注意的是,除非有很多 CPU/MCU,否则拥有许多硬件定时器(供软件使用)是没有用的,因为使用软件定时器更容易。
在 x86 上,HPET 计时器实际上最多由 32 个计时器组成,每个计时器有 8 个比较器,从软件 POV 中可以看到总共 256 个计时器。
这个想法是将每个计时器分配给特定的应用程序。

操作系统中的应用程序不直接使用硬件定时器,因为可能有很多应用程序但只有一两个定时器。
所以操作系统所做的是共享计时器。
它通过对定时器进行编程以每 X 个时间单位生成一个中断并为此类事件注册一个 ISR(中断服务程序)来实现此目的。
当线程/任务/程序设置计时器时,操作系统使用绝对过期时间作为键将计时器信息(定期 vs 一次性、周期、剩余滴答和回调)附加到优先级队列(参见 Peter Cordes 评论)下面)或简单操作系统的列表。
每次调用 ISR 时,操作系统都会查看队列并查看顶部的元素是否已过期。

当软件定时器到期时会发生什么取决于操作系统。
一些嵌入式和小型操作系统可能会直接从 ISR 的上下文中调用计时器的回调。
如果操作系统没有真正的线程/任务(以及上下文切换)的概念,这通常是正确的。
其他操作系统可能会将计时器的回调附加到“即将调用”函数的列表中。
该列表将由专门的任务遍历和处理。如果启用了计时器任务,FreeRTOS 就是这样做的。
这种方法使 ISR 保持较短,并允许以较短的周期对硬件定时器进行编程(在许多体系结构中,在 ISR 中中断被忽略,无论是由 CPU 自动屏蔽中断还是由中断控制器)。
IIRC Windows 做了类似的事情,它在设置软件计时器刚刚过期的线程的上下文中发布 APC(异步过程调用)。当线程被调度时,APC 将(作为窗口消息的形式与否,取决于所使用的特定 API)调用回调。如果线程正在等待计时器,我认为它只是设置为就绪状态。在任何情况下,它都不会立即安排,但它可能会获得优先级提升。

在ISR将返回仍然是依赖于操作系统。
操作系统可能会继续执行被中断的线程/任务,直到它被调度出去。在这种情况下,您不会在第 3 步之后立即执行第 4 步,而是线程 3 将运行直到其量程到期。
另一方面,操作系统可能会向硬件发出 ISR 结束的信号,然后使用回调调度线程。
如果两个或更多计时器在同一滴答中到期,则此方法不起作用,因此更好的方法是执行重新调度,让调度选择最合适的线程。
调度还可以考虑在软件定时器的创建期间由线程给出的其他提示。
操作系统也可能只是切换上下文,执行回调并返回到继续查看队列的 ISR 上下文。
操作系统甚至可以根据计时器的时间段和其他提示进行任何操作。

因此它的工作方式与您想象的非常相似,只是在计时器到期时可能不会立即调用线程。

更新计时器并不昂贵。
虽然总的工作量并不多,但定时器 ISR 的目的是在一秒钟内多次调用。
事实上,我什至不确定操作系统是否允许您创建如此大量(500k)的计时器。
Windows 可以管理很多计时器(及其支持线程),但可能不是 500k。

拥有大量计时器的主要问题是,即使每个计时器只执行很少的工作,执行的总工作量也可能太多,无法跟上滴答的速度。
如果每 X 个单位(例如 1ms)的时间 100 个计时器到期,则您有 X/100 个时间单位(例如 10us)来执行每个回调,并且回调的代码可能太长而无法在该时间段内执行。
发生这种情况时,回调的调用频率将低于预期。
更多的 CPU/内核将允许一些回调并行执行并减轻压力。

一般来说,如果它们以不同的速率运行,您需要不同的计时器,否则,一个单一的计时器可以遍历充满工作/数据元素的数据结构。
如果您的任务受 IO 限制(文件、网络、输入等),则多线程可以提供并发性;如果您拥有多处理器系统,则多线程可以提供并行性。

  • 您真的会向上计数并进行比较,而不是制作一个向下计数器,通过高位的借位输出来检测换行吗?您仍然必须将原始值存储在某个地方以供定期计数器使用,但不是一次性的。无论哪种方式,它都可以在 N 个时钟周期后断言一个信号,但我想一个区别是您是否可以读出已经计数的周期与下一次中断之前剩余的周期。因此,具有该功能的硬件计时器可能意味着一种设计或另一种设计。 (2认同)
  • *每次调用 ISR 时,操作系统都会遍历列表并减少每个软件计时器中剩余的滴答声。* - 我自己没有编写操作系统,但我认为每个计时器都有固定的截止日期会更典型(在挂钟时间或自某个参考点以来的滴答声中),因此操作系统实际上只是*比较*“现在”是否晚于任何计时器。或者将它们从排序的优先级队列中拉出来,然后对已经过去的队列采取行动,直到找到下一个未来的截止日期。无论哪种方式,实际上都没有修改每个排队的 SW 事件中的任何内容,并且希望不会遍历每个事件。 (2认同)
  • @PeterCordes FWIW,LAPIC 计时器是一个倒数计时器。它像你说的那样工作。HPET(高精度事件计时器)是一个计数,但它的 32 个计时器中的每一个都有一组 8 个比较器(因此在这种情况下计数是有意义的)。PIT 是 x86 上的传统计时器,具有三个倒计时计数器。所以我记错了。感谢您指出这一点! (2认同)