17

假设我在嵌入式环境中有一个协作调度程序。我有很多进程正在运行。我想利用看门狗计时器,以便我可以检测到进程何时因任何原因停止运行并重置处理器。

在没有 RTOS 的更简单的应用程序中,我总是会从主循环中接触看门狗,这总是足够的。但是,在这里,有许多进程可能会挂起。什么是定期触摸看门狗定时器同时确保每个进程处于良好状态的干净方法?

我在想我可以为每个进程提供一个回调函数,这样它就可以让另一个监督所有进程的函数知道它还活着。回调将传递一个参数,该参数将是任务的唯一 ID,因此监督者可以确定谁在回调。

4

5 回答 5

29

一种常见的方法是将看门狗踢给特定任务(通常是最高优先级或最低优先级,每种方法的权衡/动机),然后让所有其他任务“签入”该任务。

这边走:

  • 如果中断挂起(100% CPU),踢球任务将不会运行,你重置

  • 如果踢球任务被挂起,你重置

  • 如果另一个任务挂起,踢球任务看不到签入,踢球任务不会踢 WDG,你重置

现在当然要考虑实现细节。有些人让每个任务在全局变量中(原子地)设置自己的专用位;kicker 任务以特定的速率检查这组位标志,并在每个人都签入时清除/重置(当然还有踢 WDG。)我避开像瘟疫这样的全局变量并避免这种方法。RTOS 事件标志提供了一种更优雅的类似机制。

我通常将我的嵌入式系统设计为事件驱动系统。在这种情况下,每个任务都会阻塞在一个特定的位置——消息队列上。所有任务(和 ISR)通过发送事件/消息相互通信。这样,您不必担心任务不签入,因为它被信号量阻塞在“那里”(如果这没有意义,对不起,不写更多我无法更好地解释它)。

还有一个考虑因素——任务是否“自主”签入,或者他们是否回复/响应来自踢球任务的请求。自治 - 例如,每个任务每秒都会在其队列中接收一个事件“告诉 kicker 任务你还活着”。回复请求 - 每秒一次(或其他),kicker 任务告诉每个人(通过队列)“签到时间” - 最终每个任务都运行它的队列,获取请求并回复。对任务优先级、排队论等的考虑适用。

有 100 种方法可以给这只猫剥皮,但一个任务的基本原则是负责踢 WDG 并让其他任务集中到踢球任务。

至少还有一个方面需要考虑——在这个问题的范围之外——那就是处理中断。如果 ISR 占用 CPU(很好),我上面描述的方法将触发 WDG 重置,但是相反的情况呢 - ISR (遗憾地)意外地被禁用了。在许多情况下,这不会被捕获,并且您的系统仍然会踢 WDG,但您的系统的一部分已瘫痪。有趣的东西,这就是我喜欢嵌入式开发的原因。

于 2012-11-04T15:19:02.333 回答
7

一种解决方案模式:

  • 每个希望被检查的线程都向看门狗线程显式注册其回调,看门狗线程维护此类回调的列表。
  • 当看门狗被调度时,它可能会迭代已注册任务的列表
  • 每个回调本身都被迭代调用,直到它返回一个健康状态。
  • 在列表的末尾,硬件看门狗被踢。

这样,任何永远不会返回健康状态的线程都会停止看门狗任务,直到发生硬件看门狗超时。

在抢占式操作系统中,看门狗线程将是最低优先级或空闲线程。在协作调度程序中,它应该在回调调用之间让步。

回调函数本身的设计取决于具体的任务及其行为和周期性。每个功能都可以根据任务的需要和特点进行定制。高周期性的任务可能只是增加一个计数器,当回调被调用时它被设置为零。如果计数器在进入时为零,则自上次看门狗检查以来任务没有调度。具有低或非周期性行为的任务可能会对其调度进行时间戳记,如果该任务尚未在某个指定时间段内调度,则回调可能会返回失败。可以以这种方式监视任务和中断处理程序。此外,因为向看门狗注册是线程的责任,所以您可能有一些线程根本没有注册。

于 2012-11-04T16:58:01.247 回答
2

每个任务都应该有自己的模拟看门狗。只有当所有模拟的看门狗都没有超时时,真正的看门狗才会由高优先级的实时任务提供。

IE:

void taskN_handler()
{
    watchdog *wd = watchdog_create(100); /* Create an simulated watchdog with timeout of 100 ms */
    /* Do init */
    while (task1_should_run)
    {
        watchdog_feed(wd); /* feed it */
        /* do stuff */
    }
    watchdog_destroy(wd); /* destroy when no longer necessary */
}

void watchdog_task_handler()
{
    int i;
    bool feed_flag = true;
    while(1)
    {
        /* Check if any simulated watchdog has timeout */
        for (i = 0; i < getNOfEnabledWatchdogs(); i++) 
        {
            if (watchogHasTimeout(i)) {
                   feed_flag = false;
                   break;
            }
         }

         if (feed_flag)
             WatchdogFeedTheHardware();

         task_sleep(10);
}

现在,可以说系统受到了真正的保护,不会出现死机,甚至不会出现部分死机,而且大多数情况下,不会出现不需要的看门狗触发。

于 2013-03-12T16:00:33.397 回答
1

传统的方法是有一个具有最低优先级的看门狗进程

PROCESS(watchdog, PRIORITY_LOWEST) { while(1){reset_timer(); sleep(1);} }

并且实际的硬件计时器可能每 3 或 5 秒重置一次 CPU。

跟踪单个进程可以通过逆向逻辑实现:每个进程将设置一个计时器,其回调向看门狗发送“停止”消息。然后每个进程都需要取消前一个计时器事件并在“从队列接收事件/消息”循环中的某处设置一个新事件。

PROCESS(watchdog, PRIORITY_LOWEST) {
    while(1) { 
       if (!messages_in_queue()) reset_timer();
       sleep(1);
    }
}
void wdg_callback(int event) { 
    msg = new Message();
    send(&msg, watchdog);
};
PROCESS(foo, PRIORITY_HIGH) {
     timer event=new Timer(1000, wdg_callback);
     while (1) {
        if (receive(msg, TIMEOUT)) {
           // handle msg       
        } else { // TIMEOUT expired 
           cancel_event(event);
           event = new Timer(1000,wdg_callback);
        }
     }
}
于 2012-11-04T13:50:44.423 回答
1

其他答案已经涵盖了您的问题,我只想建议您在旧程序中添加一些内容(没有 RTOS)。不要只从 main() 无条件地踢看门狗,有可能某些 ISR 会卡住,但系统会在没有通知的情况下继续工作(Dan 提到的问题也与 RTOS 有关)。

我一直在做的是将主中断和定时器中断相关联,以便在定时器内对变量进行倒计时,直到它为零,然后从主中断检查它是否为零,然后才喂看门狗. 当然,喂食后将变量返回初始值。很简单,如果变量停止递减,你就会得到重置。如果 main 停止为看门狗供电,您将得到复位。

这个概念很容易仅适用于已知的周期性事件,但最好还是从主要做所有事情。另一个好处是乱码不太可能踢看门狗,因为您在主程序中的看门狗馈送过程已经在一些疯狂的循环中结束。

于 2013-03-14T15:06:28.657 回答