リアルタイムオペレーティングシステム
リアルタイムオペレーティングシステム (RTOS; Real-time operating system) は、リアルタイムシステムのためのオペレーティングシステム(OS)である。組み込みオペレーティングシステムに多い。OSの主要な機能である資源管理において、時間資源の優先度に基づく配分と実行時間の予測可能性を提供することに特化している、ないし、そういった機能に力を入れている。
以下、ほとんどのRTOSが持つ、基本的な機能について述べる。
RTOSは、実行コンテキストを保持するオブジェクト(Unixではスレッドと呼んでいるものに近いが、RTOSではもっぱらタスクと呼ぶ。以下タスクで統一する)と、タスク間の同期通信機構を提供する。同期通信機構により特定のタスクに待ち状態が発生したときのスケジューリングを行うのも、一般的なOSと同様RTOSの仕事であるが、このスケジューリング規則にRTOSの大きな特色がある。
RTOSへの要求には、以下のようなものが挙げられる。
- ユーザアプリケーションから、待ちが発生しないサービスコールを呼出した場合に要する最悪値
- ハードウェア割込みが発生してから、処理ルーチンを呼出すまでの最悪値
- 高優先度のタスクが確実に実行されることを保証するスケジューリング規則
RTOSは、時間制約を保証できる設計を実現するために使われる。以上の要求は、そのために必要な事項の一例である。
目次
設計上の特徴
スケジューリング
RTOSは通常マルチタスクOSで、スケジューリングはタスクの優先度に基づいて行われる。タスクの実行中(割込みハンドラやOS自身などの実行中でないということ)は、常に、実行可能状態にあるタスクのうち最高優先度のものを実行しなければならない。実行中のタスクよりも優先度が高いタスクが実行可能状態になった場合は、即座にタスク切り替えを行う。すなわち、RTOSはいわゆる「プリエンプティブ・マルチタスク」(プリエンプションを参照)でなければならない。さらにRTOSの場合は、カーネルが優先度の低いタスクによるシステムコールを実行中の場合でクリティカルセクションでなければ、優先度の高いタスクを実行する「プリエンプティブ・カーネル」でなければならない。
汎用OSのように、タスクの消費時間により優先度を変化させることは通常おこなわない。ただし、時間制約のない低優先度のタスクを複数同居させる場合など、それらのタスクでは優先度を共通とし、自発的にCPUを手放す協調的マルチタスクや、タイマ割込みにより順番に切り替えるタイムシェアリング的なスケジューリングを同居させることもある。
典型的な設計では、タスクには「実行中」「実行可能」「ブロック」の3状態がある。ほとんどのタスクはブロック状態でいることが多い。CPUは1度に1つのタスクを実行できる(実行中状態となるタスクはCPU毎に最大1つ)。単純なシステムでは実行可能なタスクのリストも短く、せいぜい2個から3個のタスクが載っていることが多い。
スケジューラの設計は重要である。実行可能タスクのリストはスケジューラのクリティカルセクション(プリエンプションが禁止され、場合によっては全割り込みも不可となる)で消費する時間を最小にするよう設計される。ただし、データ構造の選択は実行可能リスト上の最大タスク数にも依存する。
実行可能リスト上のタスク数が少ないなら、単純な双方向線形リストが最適である。状況によって実行可能タスク数が増えるなら、優先度に従ってソートされたリストを使用し、最高優先度のタスクを探すためにリスト全体を検索する必要がないようにすべきである。そうすると、あるタスクを実行可能タスクのリストに追加する際にリスト全体を探索して、そのタスクより低い優先度のタスクを見つけ、その前の位置にタスクを追加(挿入)する必要が生じる。この探索期間中ずっとプリエンプションを禁止してはいけない。探索中の真にクリティカルな期間だけプリエンプションを禁止することで、例えば探索中に割り込みが発生してより高優先度のタスクが実行可能となったら、現に実行可能リストに挿入しようとしている低優先度タスクよりも先に実行可能リストに挿入して実行するようにしなければならない。
新たな実行可能タスクをキューに登録し、最高優先度タスクの状態をリストアするのにかかる時間が非常に重要な応答時間である(フライバック時間とも呼ばれる)。よく設計されたRTOSでは、新たなタスクの実行可能キューへの挿入には(キュー上のエントリ毎に)3~20命令かかり、最高優先度タスクのリストアには5~30命令かかる。20MHzのMC68000プロセッサでは、2個のタスクが実行可能な状態でのタスク切り替え時間は20マイクロ秒である。100MHzのARMプロセッサでは数マイクロ秒となる。
高度なリアルタイムシステムでは、リアルタイムのタスク以外に非リアルタイムタスクも共存するため、実行可能リストは非常に長くなる可能性がある。そのようなシステムでは、スケジューラの実行可能リストを単純な線形リストで実装するのでは不十分である。このため、優先度毎に実行可能リストを分割することで探索処理を不要にすることもある。
タスク間通信とリソース共有
マルチタスクシステムでは、複数のタスク間でデータやハードウェアリソースを共有するという問題に対処しなければならない。一般に2つのタスクが同時に同じデータや同じハードウェアリソースにアクセスすることは危険である(ここで「危険」と言うのは、タスクが複数のデータの集合体を更新中だった場合などに、結果の一貫性が保てず、予期しない結果を生じることを意味する。他のタスクがそのデータ集合体にアクセスするのは、更新が始まる前か更新を完了した後でなければならない)。これを解決する一般的方法として以下の3種類があげられる。
汎用OSではユーザープログラムが割り込みをマスクすることはできないのが一般的である。というのもCPUモードによってユーザーができることは限られているためである。最近のCPUは割り込みマスクのレジスタや命令をユーザーモードでアクセスできないようにしている。しかし、組み込みシステムやRTOSの多くはアプリケーションをカーネルモードで実行可能であり、システムコールを効率化したり、OSの介入なしで動作環境の制御ができるようになっている。
シングルプロセッサシステムでは、アプリケーションをカーネルモードで実行して割り込みマスクの制御も可能なら、共有リソースへの同時アクセスを防ぐ方法としてはそれが最も効率的(オーバヘッドが小さい)である。割り込みがマスクされていると、実行中タスクはCPUを独占することができ、他のタスクや割り込みが制御を奪うことはできない。そのためクリティカルセクションは効率的に保護される。タスクがクリティカルセクションを抜けたとき、割り込みマスクが解除され、保留されていた割り込みがあれば、その処理が実行される。割り込みマスクは、クリティカルセクションの期間が割り込み処理遅延時間の要求指標を守る範囲内でなければならない。一般にこの方法はクリティカルセクションがソースコードでほんの数行でループが含まれていない場合のみ適用される。ハードウェアのビットマップレジスタを複数のタスクが操作する場合、この方法による排他が理想的である。
クリティカルセクションがもっと長かったりループを含んでいる場合、セマフォやOS提供のプロセス間通信機能を使用しなければならない。そのような機能はシステムコールとして実装されているのが一般的で、完了時にOSのディスパッチ処理が実行される。そのため、割り込みマスクと比較すると非常に長時間を要する可能性がある。しかし、クリティカルセクションが長い場合、割り込み遅延時間の問題があるので選択の余地はない。
2値セマフォは、ロック状態かアンロック状態である。ロックされている場合、タスクはそのセマフォを待つためのキューに登録される。一般にタスクはセマフォの待ち時間のタイムアウト値を設定できる。セマフォに関する問題として、優先順位の逆転やデッドロックがよく知られている。
「優先順位の逆転」では、高優先度タスクが低優先度タスクの持つセマフォを待つため、低優先度のタスクが先に処理される。一般的な解決策としては優先度継承や優先度上限プロトコルがある。「デッドロック」は複数のセマフォを獲得しようとする複数のタスクが存在する場合に発生する。デッドロックはセマフォの獲得順を厳密に設計することで回避するのが一般的である。ただし、同種のリソースを2個同時に獲得しなければならない場合、それぞれのセマフォの獲得順を厳密に決定することは困難である。その場合、例えばセマフォを獲得できないときにビジーウェイトもブロックもしないでエラーを返すプリミティブを用意するなどの方式がある。
タスク間のリソース共有の別の方法として「メッセージ渡し」がある。この場合、あるリソースは直接的には特定の1つのタスクのみが管理し、他のタスクがそのリソースにアクセスしたい場合は、管理タスクにメッセージを送信する。この方式でも優先順位の逆転やデッドロックは発生する可能性がある。しかし、システムが単純であればデッドロックが発生しないように設計可能であるため、性能的にはセマフォよりも不利だが、動作を予測し易い。
割り込みハンドラとスケジューラ
割り込みハンドラは最高優先度のタスクもブロックして動作する。また、RTOSはスレッド待ち時間を可能な限り最小化しなければならないので、割り込みハンドラの処理は可能な限り最小限にされている。割り込みハンドラは対応するハードウェアとのやりとりを可能な限り後回しにする。一般に必ず必要な処理として割り込みの受信確認 (acknowledge) か割り込みのディセーブル(割り込みハンドラから戻るまでに再度割り込まれないようにする)がある。割り込みハンドラは低優先度ですべき処理をキューに入れる(デバイスドライバタスクを実行可能にする)。スケジューラには割り込みハンドラから任意のタスクを実行可能状態にする機能が備わっていることが多い。
メモリアロケーション
RTOSの動的メモリアロケーションは、汎用OSよりも要求される条件が厳しい。
第一にアロケーションの速度が重要である。一般にメモリアロケーション処理では不定長のフリーなメモリブロックのリストを探索して必要なサイズのメモリブロックを得るが、RTOSでは一定時間以内にメモリアロケーションを行う必要があるため、この方式は採用できない。
また、空き領域を分割して使用していくと、空き領域の断片化(フラグメンテーション)が発生する。これによって空きメモリ自体は十分あるのにメモリを確保できなくなってプログラムが停止してしまう可能性が出てくる。デスクトップ型コンピュータでは、それなりの頻度でリブートが行われるため、ある程度の断片化は許容される。しかし、組み込みシステムでは何年もリブートしないで動作することもあるため、断片化は許容できない。
固定サイズのメモリブロックを割り当てる方式が、単純な組み込みシステムでは非常にうまく機能する。
応用分野
小規模な組み込みシステムなどに用いられることが多いが、デスクトップ分野やPDAなどの比較的大規模なものから、果てはミッションクリティカルなサーバーにまで使われている。
特にマルチコアや汎用ハードウェアに対応したLinuxのリアルタイムカーネルはニッチな市場の業界で多く使われている。たとえばマルチコアMIPSと共に通信業界で使われたMontavista Linux CGEや、リアルタイム・オーディオ処理のためのデスクトップ環境のUbuntu Studio、金融業界においては金融取引システムや高頻度取引にまで使われている。
APIから見た分類
小規模のものは独自に実装されることが多いため、仕様もそれぞれ独自であることも多い。共通規格としては、主に日本を中心として普及しているITRON、欧州を中心とする車載用を主としたOSEKがある。
リアルタイムUnixの標準として、POSIXのリアルタイム拡張POSIX 1003.1bがある。