这是一个演示如何创建 POSIX 兼容的间隔定时器的教程。
对开发人员来说,定时某些事件是一项常见任务。定时器的常见场景是看门狗、任务的循环执行,或在特定时间安排事件。在这篇文章中,我将演示如何使用 timer_create(…)创建一个 POSIX 兼容的间隔定时器。
你可以从 GitHub下载下面样例的源代码。
准备 Qt Creator
我使用 Qt Creator作为该样例的 IDE。为了在 Qt Creator 运行和调试样例代码,请克隆GitHub上的仓库,打开 Qt Creator,在 “文件File -> 打开文件或项目……Open File or Project…” 并选择 “CMakeLists.txt”:
在 Qt Creator 中打开项目
选择工具链之后,点击 “配置项目Configure Project”。这个项目包括三个独立的样例(我们在这篇文章中将只会用到其中的两个)。使用绿色标记出来的菜单,可以在每个样例的配置之间切换,并为每个样例激活在终端运行 “在终端中运行Run in terminal”(用黄色标记)。当前用于构建和调试的活动示例可以通过左下角的“调试Debug” 按钮进行选择(参见下面的橙色标记)。
项目配置
线程定时器
让我们看看 simple_threading_timer.c
样例。这是最简单的一个。它展示了一个调用了超时函数expired
的间隔定时器是如何被创建的。在每次过期时,都会创建一个新的线程,在其中调用函数expired
:
#include
这种方法的优点是在代码和简单调试方面用量小。缺点是由于到期时创建新线程而增加额外的开销,因此行为不太确定。
中断信号定时器
超时定时器通知的另一种可能性是基于 内核信号。内核不是在每次定时器过期时创建一个新线程,而是向进程发送一个信号,进程被中断,并调用相应的信号处理程序。
由于接收信号时的默认操作是终止进程(参考 signal手册页),我们必须要提前设置好 Qt Creator,以便进行正确的调试。
当被调试对象接收到一个信号时,Qt Creator 的默认行为是:
- 中断执行并切换到调试器上下文。
- 显示一个弹出窗口,通知用户接收到信号。
这两种操作都不需要,因为信号的接收是我们应用程序的一部分。
Qt Creator 在后台使用 GDB。为了防止 GDB 在进程接收到信号时停止执行,进入 “工具(Tools) -> 选项Options” 菜单,选择 “调试器Debugger”,并导航到 “本地变量和表达式Locals & Expressions”。添加下面的表达式到 “定制调试助手Debugging Helper Customization”:
handle SIG34 nostop pass
Sig 34 时不停止
你可以在 GDB 文档中找到更多关于 GDB 信号处理的信息。
接下来,当我们在信号处理程序中停止时,我们要抑制每次接收到信号时通知我们的弹出窗口:
Signal 34 弹出窗口
为此,导航到 “GDB” 标签并取消勾选标记的复选框:
定时器信号窗口
现在你可以正确的调试 signal_interrupt_timer
。真正的信号定时器的实施会更复杂一些:
#include
与线程定时器相比,我们必须初始化信号并注册一个信号处理程序。这种方法性能更好,因为它不会导致创建额外的线程。因此,信号处理程序的执行也更加确定。缺点显然是正确调试需要额外的配置工作。
总结
本文中描述的两种方法都是接近内核的定时器的实现。不过,即使 timer_create(…)函数是 POSIX 规范的一部分,由于数据结构的细微差别,也不可能在 FreeBSD 系统上编译样例代码。除了这个缺点之外,这种实现还为通用计时应用程序提供了细粒度控制。