RTOSスケジューリング方式の比較
RTOSに備わる中核機能の一つが「スケジューラ」です。複数のタスクをどのように切り替えるかを決めるスケジューラの仕組みを理解することは、リアルタイム要件を満たした設計には欠かせません。スケジューリング方式の選択はシステムの応答性や周期精度に関係するため、要件定義の段階から意識しておくべき設計判断の一つです。
この記事では、プリエンプティブと協調型という2つの方式をレイテンシとジッタの観点から比較し、選択基準を整理します。
- RTOSのスケジューリング|次にどのタスクをCPUで実行するか
- RTOSスケジューリング方式①:プリエンプティブ
- RTOSスケジューリング方式②:協調型
- 2方式の比較:レイテンシとジッタで見る違い
- RTOSスケジューリング方式の選択基準
- まとめ
RTOSのスケジューリング|次にどのタスクをCPUで実行するか
スケジューラは、「次にどのタスクをCPUで実行するか」を決める機能です。ある瞬間にCPUが実行できるタスクは一つだけで、スケジューラがタスクの優先度と状態を見て切り替えのタイミングを管理します。
タスクは常に「実行中(Running)」「実行可能(Ready)」「ブロック中(Blocked)」「中断(Suspended)」のいずれかの状態にあります。この状態遷移を監視しながら「どのタスクを、いつ動かすか」を判断するルールがスケジューリング方式です。
スケジューリング方式は大きく「プリエンプティブ(先取り型)」と「協調型(ノンプリエンプティブ)」の2つに分類されます。それぞれの動作原理と特性を理解することが、適切な方式の選択につながるため、次章からそれぞれの違いについて確認しましょう。
RTOSスケジューリング方式①:プリエンプティブ
プリエンプティブ方式は、現在実行中のタスクよりも高い優先度のタスクが実行可能になった瞬間に、強制的にコンテキストを切り替える方式です。FreeRTOSをはじめ多くのRTOSが採用しているスタンダードな方式です。
高優先度タスクが即座にCPUを奪う
プリエンプティブ方式では、高優先度タスクが実行可能になった瞬間にスケジューラが即座にコンテキストを切り替え、優先度の一番高いタスクを動かします。ISR(割り込みハンドラ)がキューやセマフォで通知した高優先度タスクがほぼリアルタイムに起動でき、厳しいレイテンシ要件を満たしやすくなります。
ただし、優先度設計を誤ると低優先度のタスクが長時間CPUを得られないスタベーション(飢餓状態)に陥るリスクがあります。そのため、各タスクの優先度は用途と応答要件を踏まえて慎重に決めることが重要です。
タイムスライスで同一優先度タスクを公平に実行する
同一優先度のタスクが複数ある場合、タイムスライスと呼ばれる1tick(システムの単位時間)ごとの制限時間を割り当て、ラウンドロビンスケジューリングによって実行タスクを順番に切り替えることが可能です。FreeRTOSではconfigUSE_TIME_SLICINGを「1」に設定すると有効になります。
高優先度タスクが優先的に実行される環境で、低優先度タスクを実行できる時間が少ないときにでも、ある程度の時間ですべての低優先度タスクに動作できる時間が与えられます。この点はメリットの一つといえるでしょう。
ただし、この機能はラウンドロビンスケジューリングを採用した構成でだけ有効であり、実装しない選択をするケースもあります。
RTOSスケジューリング方式②:協調型
協調型(ノンプリエンプティブ)方式は、実行中のタスクが自発的にCPUを手放すまでそのタスクが動き続ける方式です。コンテキスト切り替えのタイミングを開発者が制御できるため、動作を予測しやすい特性があります。
タスクが自発的にCPUを手放すまで実行が続く
協調型では、タスクがtaskYIELDにより明示的にCPUを譲るか、vTaskDelayなどでブロック状態になるまでCPUを占有し続けます。割り込みは発生しますが、タスクレベルでの切り替えは自発的な手放しのタイミングに限られており、開発者が制御できるため動作は予測しやすいでしょう。
一方で、yield呼び出しの漏れや処理の長期化が起きると、他のタスクが一切動かなくなるリスクもあります。そのため、各タスクが定期的にCPUを手放すなど、規律ある設計が前提となります。
コンテキスト切り替えが明確な反面、タスク設計に規律が必要
協調型の設計では「適切なタイミングでCPUを手放す」という規律が必要です。コンテキスト切り替えのタイミングを予測しやすい反面、各タスクが定期的にCPUを手放さなければ他のタスクが動作しないという制約があります。
ベアメタルのsuperloop設計と比べると、タスク単位でロジックを分離できる分、保守性は向上します。一方で、厳しいレイテンシ要件をタスクで吸収するのは協調型では難しいため、そのような設計をとらなければならない場合はISRで処理を完結させる設計が必要です。
一つのタスクが処理を抱え込みすぎないよう設計することが、協調型方式を機能させるポイントとなります。
2方式の比較:レイテンシとジッタで見る違い
スケジューリング方式を選択する際に重要な指標が「レイテンシ」と「ジッタ」です。2方式でこれらがどう異なるかを解説します。
| 指標 | プリエンプティブ | 協調型 |
|---|---|---|
| 割り込みレイテンシ | 割り込み禁止区間の長さで決まる | 割り込み禁止区間の長さで決まる |
| タスクレイテンシ | コンテキスト切り替えコストが加わるが短い | 自発的な手放しを待つため長くなりやすい |
| ジッタ | 優先度による制御が効くため小さくできる | 他タスクの処理時間に依存して大きくなりやすい |
割り込みレイテンシ:割込み禁止区間の長さで決まる
ハードウェア割り込み発生から、ISRの最初の命令が実行されるまでの時間が割り込みレイテンシです。クリティカルセクション(割り込み禁止区間)が長いほど悪化するため、禁止区間を短く保つことが最短レイテンシの条件になります。
この点はプリエンプティブ・協調型いずれも共通です。
タスクレイテンシ:コンテキスト切り替えコストが加わる
ISR完了後に対応タスクが動き出すまでの時間がタスクレイテンシです。プリエンプティブではカーネルがコンテキストを保存・復元するコストが上乗せされます。
それでも高優先度タスクを即座に起動できるため、プリエンプティブ方式の方がリアルタイム応答性は高くなります。
ジッタ:協調型では大きくなりやすい
周期タスクの起動タイミングが理想値からどれだけずれるかを表す指標がジッタです。協調型では他のタスクが自発的にCPUを手放すまで待たされるため、周期精度がそのタスクの処理時間に依存し、ジッタが拡大しやすくなります。
たとえば、モーター制御や音声処理のように周期精度が品質に直結する用途ではプリエンプティブ方式を採用し、ジッタをコンテキスト切り替えコストの範囲内に抑えることが基本です。
RTOSスケジューリング方式の選択基準
RTOSのスケジューリング方式を選択する際は、次の3点を軸に判断すると良いでしょう。
- 最悪ケースのレイテンシがデッドラインを満たすか
- 同一優先度タスクの公平性が必要か
- コンテキスト切り替えコスト(オーバーヘッド)を許容できるか
FreeRTOSを含む多くのRTOSは、プリエンプティブ+タイムスライスが基本設定となります。厳しいリアルタイム要件がある組み込みシステムでは、プリエンプティブを選択するケースがほとんどです。
プリエンプティブが適しているのは、センサの割り込み処理や制御ループのように「いつ発生するかわからないイベントに即座に応答しなければならない」場面です。
一方、協調型は処理の切り替えタイミングを開発者が明示的に管理できます。そのため、リソースが非常に限られたシンプルなシステムや、タスク間の実行順序を厳密にコントロールしたい場面では選択肢となるでしょう。
まとめ
プリエンプティブ方式は高優先度タスクへの即時応答が強みであり、レイテンシとジッタの制御がしやすい反面、コンテキスト切り替えのオーバーヘッドが生じます。一方、協調型はコンテキスト切り替えのタイミングを開発者が制御できるため、設計者の意図どおりの動作を実装することが容易です。
スケジューリング方式の選択に迷った際は、レイテンシ要件・タスクの公平性・オーバーヘッドの3点を軸に、プロジェクトの要件に合った方式を選んでください。


