RTOSのタスク設計
RTOSを使う開発では、タスクの分割・優先度・駆動方式の選択が設計の品質を左右します。タスクの数や役割が適切でなければ、スタックの肥大化や処理の飢餓、デバッグの困難といった問題につながりかねないからです。
そこでこの記事では、RTOSタスク設計の基本から気を付けるべきポイントを整理していきます。
- RTOSのタスクとは|独立したスタックを持つ実行単位
- RTOSタスク分割の2つの切り口
- RTOSタスクの優先度設計
- 周期駆動 vs イベント駆動の選択
- RTOSのタスク間通信・同期のAPI選択
- RTOSタスク設計で踏みがちな5つの落とし穴
- まとめ
RTOSのタスクとは|独立したスタックを持つ実行単位
RTOSのタスクはLinuxのスレッドに概念的に近い存在です。独立したスタックを持ち、ヒープやグローバル変数を共有しながら並行動作する単位として動きます。ただし、RTOSではメモリ保護がなく、すべてのタスクが同じアドレス空間を共有する場合が多いことに注意しましょう。
また、FreeRTOSでは各タスクが独自の実行スタックを確保し、優先度にしたがってスケジューリングされます。RTOSのタスクは一般的に以下の4つの状態を持ちます。
- 実行中(Running)
- 実行可能(Ready)
- ブロック状態(Blocked)
- 一時停止中(Suspended)
スケジューラがこれらの状態と優先度を見て、次に実行するタスクを決定するという流れが基本です。
RTOSタスク分割の2つの切り口
タスクをどう分割するかは、RTOSタスク設計の中でも特に重要な判断です。主に「機能」と「定時性」の2軸で考えると良いでしょう。
機能と処理頻度の2軸でRTOSタスクを分ける
「通信タスク」「センサ読み取りタスク」「UIタスク」など、機能単位での分割が一般的なアプローチです。さらに、処理頻度が大きく異なる処理は別タスクに分離することで、各タスクの責務が明確になります。
優先度は処理頻度より、決められた時間内に必ず完了させる必要があるかといった「定時性」を基準に設計することが重要です。頻度が高くても定時性の要求が低い処理は優先度を下げ、逆に低頻度でも応答遅延が許されない処理は優先度を高く設定します。
RTOSタスクを増やすほどスタックとオーバーヘッドが膨らむ
タスクを増やすとスタック消費量・コンテキストスイッチのオーバーヘッドの増加や、デバッグの複雑さが増します。タスク数は「最小限から設計し、必要になったら追加する」という方針で臨むことが現実的です。
RTOSタスクの優先度設計
優先度設計は、リアルタイム要件を満たすための根幹となる設計判断です。ここでは、意識すべき点を2つ挙げます。
応答要求が厳しい処理ほど優先度を高くする
基本原則は「応答時間の要求が厳しいほど優先度を高くする」ことです。たとえば、モータ制御タスクのように数ms単位の応答が求められる処理は高優先度に置く必要があります。
一方で、ログ送信タスクのようにある程度遅延が許容される処理は低優先度に置きます。
優先度は3〜5段階に絞ると管理しやすい
優先度の段階数を多くすると管理が困難になります。実務では「高(Hard RT)」「中(Soft RT)」「低(バックグラウンド)」の3段階、または細分化しても5段階程度に絞ることが現実的といえるでしょう。
周期駆動 vs イベント駆動の選択
タスクの駆動方式は大きく「周期駆動」と「イベント駆動」の2種類です。処理の性質に応じて使い分けることが設計のポイントとなります。
| 駆動方式 | 適した用途・特徴 |
|---|---|
| 周期駆動 | センサ読み取りや制御ループなど「決まった周期で処理をおこなう」場合に適切。xTaskDelayUntilやタイマ割り込みによる通知で周期の精度を確保する。 |
| イベント駆動 | 通信受信や操作入力など「不定期に発生するイベントに応じて処理する」場合に適切。キュー待ち・セマフォ待ちでCPUを無駄に消費しない。割り込みが起点になるケースも多い。 |
周期駆動:ドリフトなく周期を維持する
センサ読み取りや制御ループなど「決まった周期で処理をおこなう」場合に有効です。xTaskDelayUntilを使って前回起動時刻を基点に次の周期を計算することで、処理時間の影響を受けずに累積ドリフトを防げます。
イベント駆動:キュー待ちでCPUを無駄に消費しない
通信受信や操作入力など「不定期に発生するイベントに応じて処理する」場合に有効です。タスクはキューやセマフォを待機することでブロック状態となり、CPUを解放します。
イベントの起点はキュー受信に限らず、ISR(割り込みハンドラ)からのセマフォ・フラグ通知といった割り込みが起点になるケースもあります。
RTOSのタスク間通信・同期のAPI選択
タスク間の通信と同期には複数のAPIが用意されています。用途に応じて適切なものを選択することが重要です。
| API | 主な用途 | 特徴 |
|---|---|---|
| キュー | タスク間・ISR→タスクのデータ受け渡し | コピーセマンティクス。バッファサイズと深さを設計する必要がある。 |
| バイナリセマフォ | タスク間、またはISRとタスクの同期 | FromISR系APIを使ってISRから安全にポストできる。 |
| イベントフラグ(μITRON系) | タスクに対して、イベントの有無だけを通知 | set_flg/wai_flgなどを用いて、複数ビットのイベントを効率よく管理できる。 |
| メールボックス(μITRON系) | タスク間のメッセージ受け渡し | snd_mbx/rcv_mbxでメッセージのポインタを受け渡す。データ本体はコピーせずポインタだけ渡すため軽量 |
| イベントグループ(FreeRTOS) | 複数イベントの待ち合わせ | 複数のビットフラグを一つのグループにまとめ、AND/OR条件で待機できる。 |
| タスク通知(FreeRTOS) | 1対1の軽量な通知 | xTaskNotify/xTaskNotifyFromISRで送信先タスクに直接通知。セマフォやキュー、イベントグループより高速で省メモリ |
| ミューテックス | 共有資源の排他制御 | 優先度継承機能あり。通知目的には使わない。 |
キュー:タスク間・ISR→タスクのデータ受け渡しに使う
ISR→タスク、タスク→タスクのデータ受け渡しにキューを使います。キューはコピーセマンティクスでデータを渡すため、バッファサイズ(アイテムサイズ)と深さ(格納できるアイテム数)を適切に設計することが重要です。
通知にはフラグ・メールボックス、複数イベントの待ち合わせにはイベントグループ
単純なイベント通知にはイベントフラグ(μITRON系のset_flg/wai_flgなど)やFreeRTOSのタスク通知、メールボックスを使います。複数のイベントを同時に待つ場合はFreeRTOSのイベントグループが有効です。
一方、μITRON系のメールボックスはメッセージ(ポインタ)の受け渡しに使う仕組みです。通知とともに、タスクに対して必要な情報を付加して渡すことができます。
なお、ミューテックスは通常、共有資源の排他制御に使うものであり、通知目的には使いません。
RTOSタスク設計で踏みがちな5つの落とし穴
RTOSタスク設計では、経験が浅いうちは同じパターンの問題を繰り返しがちです。ここでは代表的な5つの落とし穴を症状・対策のセットで見ていきましょう。
落とし穴①:全処理を1タスクに集中させる
すべての処理を一つのタスクに詰め込むと、RTOSのマルチタスク機構を活かせません。処理が増えるにつれタスクが肥大化し、優先度制御も意味をなさなくなります。機能と定時性を軸にタスクを分割することで解消できます。
落とし穴②:ISRに重い処理を詰め込む
ISR内で長大な処理をおこなうと、他の割り込みの応答遅延を招きます。ISRは「最小限の処理だけ」を徹底し、重い処理はタスクに委譲することが原則です。
落とし穴③:優先度の設計ミスによるタスクの飢餓
優先度の設定を誤ると、低優先度タスクが高優先度タスクに常に割り込まれてCPU時間を全く得られない「飢餓(Starvation)」が発生します。定時性の要求を軸に優先度を設計し、低優先度タスクにも定期的にCPU時間が回ることを確認しましょう。
また、優先度の設計ミスによって引き起こされるものとして「優先度逆転」が挙げられます。ミューテックスの競合等をきっかけに、高優先度タスクが低優先度タスクよりも後に動くという逆転した状態が発生する事象です。
発生メカニズムや対策の詳細は「優先度逆転(Priority Inversion)を『再現→検出→対策』まで解説」で取り上げているため、あわせてご参照ください。
落とし穴④:グローバル変数を排他制御なしで共有する
タスク間でグローバル変数を直接共有すると、コンテキストスイッチのタイミングによってデータが壊れます。タスク間の共有データにはキューやミューテックスを介してアクセスすることが原則です。
落とし穴⑤:タスク内の長時間割り込み禁止による割り込み処理の遅延
タスク内でクリティカルセクション(割り込み禁止区間)を長く取りすぎると、その間に発生した割り込みの処理が遅延し、リアルタイム性が損なわれます。また、通信を受信する場合などでは、通信の取りこぼしが発生する可能性があります。
割り込み禁止区間は最小限に留め、やむを得ず長くなる場合は、共有資源がタスク間だけで使われるならミューテックスへの置き換えを検討するのが良いでしょう。さらに、ISRも関与するなら割り込み優先度を分割し、クリティカルな割り込みだけは許可する方式への切り替えを検討しましょう。
まとめ
RTOSタスク設計では、機能と定時性による分割・優先度の適切な設定・駆動方式の選択・通信APIの正しい使い分けが品質を決定します。実際に設計を進める際は、5つの落とし穴を意識しながら進めていきましょう。
最小限のタスク数から設計し、必要に応じて段階的に拡張していくアプローチが現場では効果的となるため、参考にRTOSのタスク設計を進めてみてはいかがでしょうか。


