介绍
在多线程环境中工作时,拥有任务监视器是实现稳健性、安全性和稳定性的最有用和最关键的组件之一。本质上,任务监视器确保系统中的所有其他任务都按预期正常工作。如果任务停止使用任务监视器签入,任务监视器可以识别违规并执行各种操作。一个非常基本的操作是允许看门狗超时并重置设备。
我从事的几乎每一个产品都包含某种类型的任务监视器,并且在各种项目和多年的工作中改进了实施,我觉得这对社区很有用。
背景
许多操作系统都内置了用于任务监控的功能(例如 linux)。然而,许多较小的实时操作系统(例如,FreeRTOS)没有此功能。此实现旨在用于可能没有内置功能的小型嵌入式系统。
特征
- 监控任意数量的任务
- 每个任务可以有一个唯一的超时时间
- 任务监视器将发送一条消息让任务响应
- 或者任务可以随时向任务监视器发送消息,而无需响应特定消息
- 任务监视器自动确定正在签入的任务(无需跟踪或发送任务 ID)
- 看门狗只需要在一个地方进行管理
- 事件驱动和消息驱动任务的理想选择
- 占地面积小
- 可用于非常小的嵌入式系统(例如,如果需要,可以在 8 位微上使用 FreeRTOS)
- 与 RTOS 无关(可与任何带有少量更新的 RTOS 一起使用)
- 在该任务超时期间,消息只会在每个周期发出一次(例如,任务超时为 100 毫秒,然后发出的消息最多每 100 毫秒发生一次,无论收到响应的速度有多快)
- 接收到任务签入的主动消息将自动重置时间,因此消息不会不必要地发送给任务
使用代码
使用现有任务/线程中的功能只需要两个基本功能。
首先,任务需要向任务监视器注册自己。这应该作为任务/线程初始化的一部分,或在任务/线程开始时完成。
- 第一个参数是任务监视器将等待来自任务的响应(来自签入请求)的最长时间。
- 第二个参数是指向检查调用的函数指针,任务监视器用于在发出检查请求时向任务发送消息。
<span style="color:#111111"><span style="background-color:#ffffff"><span style="color:#000000"><span style="background-color:#fbedbb">TaskMonitor::Register(TIMEOUT, &TaskCheckin);</span></span></span></span>
另一个主要功能是在需要时使用任务监视器进行检查。
<span style="color:#111111"><span style="background-color:#ffffff"><span style="color:#000000"><span style="background-color:#fbedbb">TaskMonitor::TaskCheckin();</span></span></span></span>
本文链接的代码显示了以不同方式使用任务监视器的四个不同任务的示例用法。任务可以响应签入请求,或根据需要抢先签入。
任务监视器将在每个超时时间段(注册期间提供的时间)向已注册任务发送一次请求TIMEOUT
。如果已注册的任务在任务监视器发送请求之前向任务监视器发布签入,则计时器将重置并且任务监视器将不会发送请求,直到另一个完整的超时时间过去。
任务监控任务
任务监视器功能依赖于使用的操作系统,并且需要有自己的任务/线程来操作。因此,开发人员需要创建和启动任务监视器任务作为其余系统任务初始化的一部分。例如,在 FreeRTOS 中:
<span style="color:#111111"><span style="background-color:#ffffff"><span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#0000ff">void</span> StartTaskMonitor(<span style="color:#0000ff">void</span> *argument)
{
TaskMonitor taskMon;
taskMon.Initialize();
taskMon.Run();
}
<span style="color:#0000ff">void</span> app_main(<span style="color:#0000ff">void</span>)
{
TaskHandle_t taskHandle = <span style="color:#0000ff">nullptr</span>;
<span style="color:#008000"><em>// Task monitor
</em></span> xTaskCreate(&StartTaskMonitor, <span style="color:#800080">"</span><span style="color:#800080">TaskMonitor"</span>, TASK_MONITOR_STACK_SIZE,
NULL, TASK_MONITOR_PRIORITY, &taskHandle);
}</span></span></span></span>
操作系统移植
任务监视器也使用一些操作系统调用来操作。因此,创建TargetPort.hpp和TargetPort.cpp是为了轻松移植到正在使用的不同操作系统。用于移植的主要项目是消息队列操作、任务识别操作和看门狗操作。
违规行为
当任务监视器确定任务违反了签入的时间限制时,它会采取行动。默认操作是执行以下操作:
<span style="color:#111111"><span style="background-color:#ffffff"><span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#008000"><em>// check for expired task checkins... if there was a violation,
</em></span><span style="color:#008000"><em>// we can print the name of the violator and then wait until the watchdog bites
</em></span><span style="color:#0000ff">if</span> (monInfo.elapsedTimeWaitingForResponse <span style="color:#0000ff">></span> monInfo.timeout)
{
LOG_ERROR(<span style="color:#800080">"</span><span style="color:#800080">TaskMonitor"</span>, <span style="color:#800080">"</span><span style="color:#800080">Task: %s - Exceeded Checkin Time.
Elapsed time %u ms, Timeout %u ms"</span>,
GetTaskName(taskId), monInfo.elapsedTimeWaitingForResponse,
monInfo.timeout);
<span style="color:#0000ff">while</span>(<span style="color:#000080">1</span>) {}
}</span></span></span></span>
while(1) {}
允许任务永远旋转,直到看门狗重置设备。可以更新此代码以执行用户想要的任何操作:
<span style="color:#111111"><span style="background-color:#ffffff"><span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#008000"><em>// Designers can also replace the above with their own logic that can reset tasks,
</em></span><span style="color:#008000"><em>// end tasks, or take other actions
</em></span><span style="color:#008000"><em>// based on violations. Example below:
</em></span><span style="color:#008000"><em>// if (monInfo.elapsedTimeWaitingForResponse > monInfo.timeout)
</em></span><span style="color:#008000"><em>// {
</em></span><span style="color:#008000"><em>// // Do action, such as restart the task that violated the timeout
</em></span><span style="color:#008000"><em>// }</em></span></span></span></span></span>