iTRONは仕様であって製品としてのOSではないですが、iTRON系のOSからFreeRTOSへの乗り換え、またはその逆を念頭に両者を比較します。
- メモリ割り当て
- タスク
- イベントフラグ
- セマフォ
- ミューテックス
- キュー/データキュー
- キューセット
- メールボックス
- メモリプール
- ソフトウエアタイマー/周期ハンドラ
- カーネル状態
- 割り込み処理
メモリ割り当て
FreeRTOS,iTRONとも、動的か静的かを選択できます。動的の場合はタスクなどのオブジェクトのメモリをヒープ領域から確保し、静的の場合はコンパイル時に領域が決まります。
FreeRTOSでは、動的か静的かをコンフィグレーションで選択し、静的の場合は割り当て処理をアプリケーションで(静的になるように)実装します。生成時は、xEventGroupCreateStaticのように、~StaticというAPIを使います。
iTRONではアプリケーションでの実装は不要で、コンフィグレーションファイルに静的APIを記述するという形です。静的APIは元のAPI名をすべて大文字にしたもので、cre_tskならCRE_TSKになります。OSによってはGUIで設定できたりします。
起動時に一通り生成した後は、生成も削除もしないというケースであれば、動的も静的も実質的に同じです。
タスク
状態
FreeRTOS | iTRON | |
---|---|---|
実行状態 | Running | Running |
実行可能状態 | Ready | Ready |
待ち状態 | Blocked | Waiting |
強制待ち状態 | Suspended | Suspended |
二重待ち状態 | × | Waiting-Suspended |
休止状態 | × | Dormant |
×は(FreeRTOSには)存在しない状態で、それ以外は基本的に同じです。
待ち状態はイベントフラグやセマフォなどによる待ちで、システムコールで(自発的に)待ちに入ります。強制待ち状態は、他のタスクが待ちに入らせるもので、当のタスクは処理の状況に関わらず停止します。
二重待ち状態は、待ち状態のタスクをSuspendした状態です。FreeRTOSではこれもSuspendedです。アプリケーションから見れば、動作はFreeRTOS,iTRONとも同じです。イベントフラグで待っているタスクをSuspendしたとして、その状態でフラグがセットされても、Resumeされない限り実行可能にはなりません。
そもそもSuspend自体使うことが稀だと思うので、どちらがいいということもないです。
アイドルタスク
FreeRTOS | iTRON | |
---|---|---|
アイドルタスク | あり | OSによる |
優先度 | 0以上 大きい方が優先 |
1以上 小さい方が優先 |
アイドルタスクは、実行可能なタスクがないときに実行される、OS内部で生成されるタスクです。iTRONは実装依存ですが、ない場合はアプリケーションでアイドルタスクをつくればいいだけです。
基本的には何もしないタスクで、他のタスクが実行可能になるまでCPU時間を消費します。電池など消費電力の制約がシビアな環境では、ハード的にスリープ(クロックを停止)したりもします。
優先度
FreeRTOS | iTRON | |
---|---|---|
値 | 0以上 | 1以上 |
順序 | 大きい方が優先 | 小さい方が優先 |
FreeRTOSの最低優先度0はアイドルタスクの優先度です。他のタスクも使用できますが、特別な理由がなければ使わない方が無難です。
ちなみに、優先度は他のタスクよりも大きいか小さいかだけが意味を持ちます。優先度1のタスクと優先度10のタスクにおいて、他にタスクがなければ、後者を優先度100にしたとしても動作は変わりません。
スケジューリング
FreeRTOS,iTRONとも優先度ベースのスケジューリング方式であって、優先度の高いタスクが実行可能な間は、それよりも低優先度のタスクはどれだけ待っても実行状態(Running)になりません。
同じ優先度のタスクの間では、FreeRTOSはデフォルトでタイムスライシング(ラウンドロビン)方式です。自発的に待ちに入らなくても、ティック間隔(システムタイマーの周期)で次のタスクに切り替わります。
iTRONは先に実行可能になったタスクが優先で、そのタスクが待ち状態になるまでは次のタスクには変わりません。
FreeRTOSではコンフィグレーション(configUSE_TIME_SLICING = 0)でiTRONと同じ方式に変更可能、iTRONはOS自体にタイムスライシングの機能はありませんが、API(rot_dtq)を使ってアプリケーションでラウンドロビンを実現することはできます。
個人的には、リアルタイムシステムでラウンドロビンでないと困ることはほとんどないと思うので、FreeRTOSの方がいいと言うほどのこともないです。
イベントフラグ
FreeRTOS,iTRONとも、ひとつのイベントフラグを複数のタスクで待つことができます。ただし、そのときの動作は若干異なります。
FreeRTOS | iTRON | |
---|---|---|
生成 | xEventGroupCreate | cre_flg |
セット | xEventGroupSetBits xEventGroupSetBitsFromISR |
set_flg iset_flg |
ウエイト | xEventGroupWaitBits | wai_flg |
複数のタスク | 可 特に指定不要 |
可 生成時にTA_WMULを指定 |
起床と同時に ビットをクリア |
可 ウエイト時に指定(xClearOnExit) |
可 生成時にTA_CLRを指定 |
起床順 | タスク優先度順 | タスク優先度またはウエイト順 生成時にTA_TPRIまたはTA_TFIFOを指定 |
クリア指定時の 起床タスク |
すべて | 先頭のタスク |
FreeRTOSでは複数のタスクが待っていた場合、すべてのタスクが実行可能になり、現在実行中のタスクと合わせて優先度の最も高いタスクに切り替わります。iTRONでは、TA_CLRの場合、待ち行列の先頭タスクだけが実行可能になります。すべてのタスクを起床するには、TA_CLRを付けずにイベントフラグを生成し、最後に起床したタスクがクリア(clr_flg)します。「最後に」や「クリア」はアプリケーションでやる必要があるので、iTRONは少し不親切です。
また、割り込みサービスルーチン(ISR)からイベントフラグをセットする場合、FreeRTOSでは(OS内部で生成される)デーモンタスクが必要になります。
ISRからxEventGroupSetBitsFromISRをコールすると、実際にはデーモンタスクを起床、デーモンタスクが待ちタスクを起床、という流れです。デーモンタスクを生成しないコンフィグレーションの場合は、ISRからイベントフラグをセットすることはできません。iTRONではこのような制約やデーモンタスクなるものはないので、iTRONの方がわかりやすいです。
FreeRTOSがこのようにしている理由は、リアルタイムOSとして応答性を保証するするためです。
待ちタスクの数には上限がないので、
- 数が多くなると、それだけ待ちを解除するための処理時間が長くなる
- 処理時間の上限が不定になる(やろうと思えば無限に大きくできる)
ISRは割り込み禁止状態なので、一定時間内の応答が保証できなくなります。それを避けるため、デーモンタスクに処理を移して、すぐに割り込み禁止状態を終了するようにしています。
iTRONはそうなっていないですが、現実的にはそんなにたくさんのタスクが待つようなアプリケーションは考えにくいし、その場合でもアプリケーションでケアすればよいということだろうと思います。
とは言え、ひとつのイベントフラグを複数のタスクが待つということ自体が稀ではないかと思います。そういう状況になったら、設計を見直してみた方がいいかもしれません。
イベントフラグ(簡易版)
FreeRTOSのEvent Groupは多機能である反面オーバーヘッドが大きいので、ひとつのタスクしか待たない場合には、Task Notificationというものをイベントフラグとして使うことができます。
FreeRTOS | iTRON | |
---|---|---|
生成 | - | cre_flg |
セット | xTaskNotify xTaskNotifyFromISR |
set_flg iset_flg |
ウエイト | xTaskNotifyWait (eAction = eSetBits) |
wai_flg |
複数のタスク | 不可 | TA_WSGLを指定したら不可 |
起床と同時にビットをクリア | 可 APIの引数(ulBitsToClearOnExit)で都度指定 |
可 TA_CLRを指定 |
Task Notificationはタスクの生成と同時に生成されます。xTaskNotifyは、待ちタスク(起床させるタスク)を指定するので、必然的に待ちはひとつのタスクのみになります。xTaskNotifyFromISRはISRの中でタスクを起床するので、デーモンタスクは不要です。
iTRONのset_flgは、フラグのIDを指定するだけで、どのタスクが起床するかはセットする側にはわかりません。wai_flgする際に、既に他のタスクがそのフラグで待っている場合はエラー(E_ILUSE)になります。
ほとんどの場合、FreeRTOSのイベントフラグ = Task Notificationと考えてよいです。
セマフォ
FreeRTOS | iTRON | |
---|---|---|
生成 | xSemaphoreCreateBinary xSemaphoreCreateCounting |
cre_sem |
解放 | xSemaphoreGive | sig_sem |
取得 | xSemaphoreTake | wai_sem |
起床順 | タスク優先度順 | タスク優先度またはウエイト順 生成時にTA_TPRIまたはTA_TFIFOを指定 |
FreeRTOSのxSemaphoreCreateBinaryは、初期値0のセマフォになります。つまり、初期状態で取得(Take)しようとブロックするので、最初に解放(Give)する必要があります。初期値を1にするにはxSemaphoreCreateCounting(uxMaxCount = uxInitialCount = 1)を使います。
セマフォを解放すると、待ちタスクがひとつ実行可能になりますが、FreeRTOSでは待ちタスクの中で最高優先度のタスクになります。iTRONではウエイト順、つまり(優先度に関係なく)先に取得しようとしたタスクから実行可能にすることもできます。
セマフォ(簡易版)
ひとつのセマフォに対して取得するタスクがひとつだけの場合は、Task Notificationを使うことができます。
FreeRTOS | iTRON | |
---|---|---|
生成 | - | cre_flg |
解放 | xTaskNotifyGive vTaskNotifyGiveFromISR |
sig_sem isig_sem |
取得 | ulTaskNotifyTake | wai_sem |
ミューテックス
FreeRTOS | iTRON | |
---|---|---|
生成 | xSemaphoreCreateMutex xSemaphoreCreateRecursiveMutex |
cre_mtx |
ロック | xSemaphoreGive | unl_mtx |
アンロック | xSemaphoreTake | loc_mtx |
多重ロック | 可 | 不可 |
優先度制御 | 優先度継承 | 優先度継承 優先度上限 |
FreeRTOSのロック,アンロックはセマフォと同じAPIです。
多重ロックはロック中のミューテックスを再度ロックしてもいいというもので、2回ロックしたら2回アンロックする必要があります。xSemaphoreCreateRecursiveMutexで生成したミューテックスのみ可能です。
優先度継承はFreeRTOS,iTRONとも同じで、高優先度のタスクがミューテックスをロックしようとしてブロックするとき、ロックしているタスクが低優先度だった場合は、そのタスクの優先度を引き上げるというものです。
タスクA(優先度a)がミューテックスをロックしているときに、タスクB(優先度b > a)が実行可能になり、そのミューテックスをロックしようとすると、ブロックして(優先度の低い)タスクAに処理が移ります。ただし、このときタスクAの優先度は一時的にbに上がります。タスクAがミューテックスをアンロックすると、タスクBに切り替わると同時にタスクAの優先度はaに戻ります。
優先度上限は、ロックする時点で自分自身の優先度を引き上げるというもので、FreeRTOSには代替となるものがなさそうです。
キュー/データキュー
FreeRTOS | iTRON | |
---|---|---|
生成 | xQueueCreate | cre_dtq |
送信 | xQueueSend | snd_dtq |
受信 | xQueueReceive | rcv_dtq |
FreeRTOSではOS内にデータサイズ×データ数の領域を確保しますが、iTRONではポインタ×データ数の領域になります。FreeRTOSではアプリケーション側で領域を確保する必要はありませんが、コピーを伴うので、大きなデータには不向きです。iTRONでは4バイト(16bit CPUなら2バイト)までのデータならFreeRTOSと同じように使えますが、それ以上の場合はアプリケーションで領域を確保する必要があります。
キューセット
FreeRTOS | iTRON | |
---|---|---|
生成 | xQueueCreateSet | × |
ウエイト | xQueueSelectFromSet | × |
複数のキュー,セマフォ,ミューテックスを同時に待つことができます。WindowsのWaitForMultipleObjectsやLinuxのselectのようなものですが、対象はキューだけです(セマフォとミューテックスはキューを使って実装されているので可)。イベントフラグとキューを同時に待つようなことはできません。ここはiTRONの欠点と言えるかもしれません。
メールボックス
FreeRTOS | iTRON | |
---|---|---|
生成 | × | cre_mbx |
送信 | × | snd_mbx |
受信 | × | rcv_mbx |
iTRONのメールボックスはリンクリストのことで、一般的にはメモリプールとセットで使われます。FreeRTOSにはメールボックスそのものはありませんが、キューで代替可能です。
メモリプール
FreeRTOS | iTRON | |
---|---|---|
生成 | × | cre_mpf |
取得 | × | get_mpf |
解放 | × | rel_mpf |
動的にメモリを確保するためのもので、1ブロックのサイズ×ブロック数の領域をOS内に確保します。~用に10バイトのブロックを10個、~用に20バイトのブロックを5個というように、用途に応じて複数のメモリプールを生成できる点が、mallocによる確保と異なります。FreeRTOSでは(セマフォ等を使って)アプリケーションで実装する必要があります。
ソフトウエアタイマー/周期ハンドラ
FreeRTOS | iTRON | |
---|---|---|
生成 | xTimerCreate | cre_cyc |
開始 | xTimerStart | sta_cyc |
停止 | xTimerStop | stp_cyc |
指定した時間が経過したらコールバック関数が呼ばれます。FreeRTOSはOS内で生成したタイマーサービスタスクから、iTRONは割り込みハンドラからです。タイマーサービスタスクは、コンフィグレーションで優先度を設定できます。
クリティカルセクション
FreeRTOS | iTRON | |
---|---|---|
割り込み禁止(ネスト可) | taskENTER_CRITICAL | × |
割り込み許可(ネスト可) | taskEXIT_CRITICAL | × |
割り込み禁止(ネスト不可) CPUロック |
taskDISABLE_INTERRUPTS | loc_cpu |
割り込み許可(ネスト不可) CPUロック解除 |
taskENABLE_INTERRUPTS | unl_cpu |
スケジューリング禁止 ディスパッチ禁止 |
vTaskSuspendAll xTaskResumeAll |
dis_dsp ena_dsp |
iTRONでは割り込み禁止のことをCPUロック、スケジューリング(コンテキストスイッチ)禁止のことをディスパッチ禁止と言います。
割り込み禁止はCPUの機能で、処理としてはCPU固有の命令を実行するだけです。ネスト不可は本当に割り込みを禁止するだけで、ネスト可は禁止した回数だけ許可しないと最終的に割り込み許可されません。iTRONにはありませんが、単純なものなのでラッパーをつくるのは簡単です。
スケジューリング禁止はOSの機能ですが、ネスト可能かを除いてFreeRTOS,iTRONとも同じです。この状態では割り込みは許可されていますが、タスクの切り替えは保留されています。割り込みが発生して、本来高優先度のタスクに切り替わる状況であっても、そのまま元のタスクに戻り、スケジューリングが許可された時点でタスクが切り替わります。FreeRTOSはネスト可能で、iTRONはネスト不可です。
すべてのタスクからアクセスする変数に対する排他制御などで使うものですが、本来セマフォやミューテックスを使うべきところでも使ってしまいがちです。
割り込み処理
iTRONでは、割り込みハンドラと割り込みサービスルーチン(ISR)が区別されています。FreeRTOSは特に区別はありません。
割り込みハンドラは割り込みによって直接ジャンプする関数で、OSは介在しません。iTRONの割り込みサービスルーチンは、カーネル管理の割り込みと呼ばれ、カーネル内で登録された割り込みハンドラから呼び出されます。割り込みサービスルーチンの目的は、割り込みフラグのクリアなど、CPU依存の処理を隠蔽することです。
一方、FreeRTOSは特に区別はなく、iTRONで言う割り込みハンドラのみです。
全体を通して
RTOSの機能はほとんど共通で、ちょっとした差異はあるものの移行は難しくないと思います。
どちらかと言うと、FreeRTOSの方がストレートかなという気はします。API名もわかりやすいです。iTRONは独自の用語を使ったり、実装依存と濁されたりするところがあります。T-Kernelを使えばいいのかもしれないですが。