17

組み込み環境に協調スケジューラがあるとします。多くのプロセスが実行されています。プロセスが何らかの理由で動作を停止したことを検出し、プロセッサをリセットできるように、ウォッチドッグ タイマーを利用したいと考えています。

RTOS を使用しない単純なアプリケーションでは、常にメイン ループからウォッチドッグに触れていましたが、これで十分でした。ただし、ここでは、ハングする可能性のあるプロセスが多数あります。各プロセスが正常であることを確認しながら、定期的にウォッチドッグ タイマーに触れるクリーンな方法は何ですか?

各プロセスにコールバック関数を提供して、すべてを監視する別の関数に、それがまだ生きていることを知らせることができると考えていました。コールバックは、タスクの一意の ID であるパラメーターを渡すため、監督者は誰がコールバックしているかを判断できます。

4

5 に答える 5

29

一般的なアプローチの 1 つは、ウォッチドッグのキックを特定のタスク (多くの場合、優先度が最も高いか最も低いか、各アプローチのトレードオフ/動機) に委任し、他のすべてのタスクをこのタスクに「チェックイン」させることです。

こちらです:

  • 割り込みがハングした場合 (100% CPU)、キッカー タスクは実行されず、リセットします。

  • キッカー タスクがハングした場合は、リセットします

  • 別のタスクがハングした場合、キッカー タスクはチェックインを認識せず、キッカー タスクは WDG をキックしません。リセットします。

もちろん、考慮すべき実装の詳細があります。一部の人々は、各タスクに独自の専用ビットを (アトミックに) グローバル変数に設定しています。キッカー タスクは、このグループのビット フラグを特定のレートでチェックし、全員がチェックインするとクリア/リセットします (もちろん、WDG のキックも行います)。RTOS イベント フラグは、より洗練されたやや似たメカニズムを提供します。

私は通常、組み込みシステムをイベント駆動型システムとして設計しています。この場合、各タスクは 1 つの特定の場所 (メッセージ キュー) でブロックされます。すべてのタスク (および ISR) は、イベント/メッセージを送信することによって相互に通信します。このように、タスクが「そこにある」セマフォでブロックされているためにチェックインしないことを心配する必要はありません (それが意味をなさない場合は、申し訳ありませんが、もっと多くのことを書かないと説明できません) )。

また、考慮事項があります-タスクを「自律的に」チェックインするか、キッカータスクからのリクエストに返信/応答しますか。自律型 - たとえば、1 秒に 1 回、各タスクはそのキューで「キッカー タスクにまだ生きていることを伝える」というイベントを受け取ります。応答要求 - 1 秒に 1 回 (または何でも)、キッカー タスクは全員に (キューを介して) 「チェックインする時間」を伝え、最終的にすべてのタスクがそのキューを実行し、要求を取得して応答します。タスクの優先度、キューイング理論などの考慮事項が適用されます。

この猫の皮を剥ぐ方法は 100 通りありますが、単一のタスクが WDG のキックを担当し、他のタスクがキッカー タスクに到達するという基本原則は非常に標準的です。

この質問の範囲外で、考慮すべき他の側面が少なくとも 1 つあります。それは、割り込みの処理です。上記の方法は、ISR が CPU を占有している場合 (良い)、WDG のリセットをトリガーしますが、逆のシナリオはどうでしょうか。多くのシナリオでは、これは捕捉されず、システムは引き続き WDG を起動しますが、システムの一部が機能しなくなります。楽しいこと、それが組み込み開発が好きな理由です。

于 2012-11-04T15:19:02.333 に答える
7

1 つのソリューション パターン:

  • チェックを必要とするすべてのスレッドは、そのコールバックをウォッチドッグ スレッドに明示的に登録します。ウォッチドッグ スレッドは、そのようなコールバックのリストを維持します。
  • ウォッチドッグがスケジュールされている場合、登録されたタスクのリストを繰り返す場合があります
  • 各コールバック自体は、正常な状態を返すまで繰り返し呼び出されます。
  • リストの最後で、ハードウェア ウォッチドッグが開始されます。

そうすれば、正常な状態を返さないスレッドは、ハードウェア ウォッチドッグ タイムアウトが発生するまでウォッチドッグ タスクを停止します。

プリエンプティブ OS では、ウォッチドッグ スレッドは優先度が最も低いスレッドまたはアイドル スレッドになります。協調スケジューラでは、コールバック コール間で譲歩する必要があります。

コールバック関数自体の設計は、特定のタスクとその動作および周期性によって異なります。各機能は、タスクのニーズと特性に合わせて調整できます。周期性の高いタスクは、コールバックが呼び出されたときにゼロに設定されるカウンターを単純にインクリメントする場合があります。開始時にカウンターがゼロの場合、タスクは最後のウォッチドッグ チェック以降にスケジュールされていません。動作が低いまたは非周期的なタスクは、スケジュールにタイムスタンプを付ける可能性があり、タスクが指定された期間スケジュールされていない場合、コールバックは失敗を返す可能性があります。この方法で、タスクと割り込みハンドラの両方を監視できます。さらに、ウォッチドッグに登録するのはスレッドの責任であるため、まったく登録しないスレッドがいくつかある可能性があります。

于 2012-11-04T16:58:01.247 に答える
2

各タスクには、独自のシミュレートされたウォッチドッグが必要です。また、実際のウォッチドッグは、シミュレートされたすべてのウォッチドッグがタイムアウトしていない場合にのみ、優先度の高いリアルタイムタスクによってフィードされます。

すなわち:

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

他の回答があなたの質問をカバーしています。古い手順(RTOSなし)に何かを追加することをお勧めします。main() のみから無条件にウォッチドッグをキックしないでください。一部の ISR がスタックする可能性がありますが、システムは予告なしに動作し続けます (Dan が言及した問題は RTOS にも関連しています)。

私が常に行ってきたのは、メインとタイマー割り込みを関連付けて、タイマー内で変数がゼロになるまでカウントダウンが行われるようにし、メインから変数がゼロかどうかを確認し、それからウォッチドッグにフィードすることでした。 . もちろん、フィード後は変数を初期値に戻します。簡単です。変数が減分を停止した場合、リセットされます。メインがウォッチドッグへの供給を停止すると、リセットされます。

この概念は、既知の定期的なイベントのみに適用するのは簡単ですが、メインからすべてを実行するよりも優れています。もう 1 つの利点は、メイン内のウォッチドッグ フィード プロシージャがワイルド ループ内で終了しているため、文字化けしたコードがウォッチドッグをキックする可能性が低いことです。

于 2013-03-14T15:06:28.657 に答える
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 に答える