リアルタイムOSの真髄
マルチタスクで複数デバイスを同時制御

 「要するにリアルタイムOSの真髄とは何なのか」と問われたら,筆者はマルチタスク機能を挙げたい。その重要性を示すために,リアルタイム・システムにおけるマルチタスク機能の意義を述べる。以下ではタスクという用語を使うが,OSによってはスレッドと呼ぶ場合もある。タスクをスレッドと言い換えても差し支えない。

 組み込みシステムは通常,モーターやセンサー,スイッチといった周辺デバイスを複数(しばしば数多く)同時に制御しなければならない。一つのプロセッサで複数の周辺デバイスを制御しようとすると,ある周辺デバイスの制御に時間がかかっているあいだ,他の周辺デバイスがおろそかになる。制御する周辺デバイスごとにプロセッサを用意すれば事は簡単だが,コスト的に許されない。そこで一つしかないプロセッサを,あたかも複数あるかのように見せる機能があるとありがたい。これが,マルチタスク機能である。

 ここでタスクとは,複数あるかのように見せかけた仮想的なプロセッサを意味する。言い換えると,タスクは並行実行の単位であり,一つのタスク中のプログラムは逐次的に実行されるのに対して,異なるタスクのプログラムの実行は並行して行われる。OSの役割は,ハードウェア・リソースを仮想化することであり,その意味では,マルチタスク機能はプロセッサを仮想化するための機能だと言える。

 プロセッサが一つの場合には,同時に実行できるプログラムは実際には一つである。つまりマルチタスク機能の実現には,複数のタスクを時分割で実行しなければならない。ここで実行するタスクを切り替える処理が必要になるが,このことをタスク切り替え(ディスパッチ,タスク・ディスパッチ)と呼ぶ。そして,ディスパッチを行うOS内のモジュールがディスパッチャである。

 これに対して,タスクの実行順序を決めることをスケジューリングと呼び,その方式をスケジューリング・アルゴリズムという。一般にスケジューリングとは,「いつ」「どのタスク」を実行するかを決定することを指す。しかし,研究レベルのOS(左ページの別掲記事)を除くと,将来どのタスクを実行するかの計画をあらかじめ立てているわけではなく,次に実行するタスクを決めることをスケジューリングと呼んでいる。

 スケジューリングを行うOSのモジュールがスケジューラである。OSの構成によっては,スケジューラとディスパッチャが一体になっており,両者を総称してスケジューラと呼ぶ場合もある。例えばLinuxはスケジューラとディスパッチャが一体になっている。そのため,スケジューリングとディスパッチを混同しやすいが,意味的には別々の処理である。この点は,区別して理解してほしい。

スケジュール・アルゴリズムで優劣が決まる

 すでに述べた通り,リアルタイムOSの大きな特徴は,スケジューリング・アルゴリズムにある。スケジューリング・アルゴリズムが,システムが時間要件を満たせるかどうかを決定づける。なぜなら,リアルタイム・システムの条件である,厳しいリソース制約(与えられた計算能力)のもとで,各タスクの処理を所定の時間内に終えなければならない。このリアルタイム性の実現には,どのような順序でタスクを実行するかが鍵を握る。

図2●プリエンプティブ・スケジューリング
低い優先度のタスクから高い優先度のタスクへの切り替えは,割り込みをトリガにする。

 ほとんどのリアルタイムOSが提供しているスケジューリング・アルゴリズムが,プリエンプティブ(後述)な優先度ベース・スケジューリングである(図2[拡大表示])。リアルタイムOSによっては,他のスケジューリング・アルゴリズムも備える場合があるが,プリエンプティブな優先度ベース・スケジューリングはほぼ例外なくサポートしていると言ってよいだろう。

 優先度ベース・スケジューリングとは,最も高い優先度を持ったタスクから順に実行する方式である。優先度の高いタスクの実行が終わるまで,優先度の低いタスクは実行しない。優先度が同じタスクがある場合,早く実行可能になった順に処理を開始するFCFS(First Come First Served)方式が普通だが,ラウンドロビン方式でスケジューリングするOSも存在する。優先度という考え方は汎用OSにもあるが,それは優先度の高いタスクに多めの処理時間を与えることを指す。優先度の高いタスクがあれば,それだけを実行するリアルタイムOSの優先度ベース・スケジューリングとは全く異なる。

 プリエンプティブとは「先取り可能」ということを意味する。優先度の高いタスクが実行可能になると,優先度の低いタスクの処理中でも,タスクを切り替えてしまうことを指す。例えば,画像を認識して自走するロボットの制御システムであれば,画像処理を行うタスクには低い優先度,衝突センサー検知時などに緊急ブレーキをかけるタスクには高い優先度を与えることになる。ここでタスクが実行可能になるキッカケは,割り込みである。つまり割り込みをキッカケに,タスク切り替えが起こることになる。

 プリエンプティブな優先度ベース・スケジューリングにも,優先度の与え方によって何通りかの方式がある。最も一般的なのは,タスクの優先度をタスク生成時に与える方法。この方式を,「静的優先度割り付け」または「固定優先度」と呼ぶ。Rate Monotonicと呼ばれている方式は,タスクの周期を優先度と見なす(周期が短いほうが優先度が高い)もので,静的優先度割り付けの一種と位置付けることができる。

 優先度を動的に変更する方式もある。代表的なのは,「Earliest Deadline Firstスケジューリング」または「デッドライン・スケジューリング」と呼ばれる方式で,デッドライン(締め切り時刻)が早いタスクから順に実行する。


次世代はOS自身がリアルタイム性を確保

 研究レベルのOSには,OSにリアルタイム性を確保する責任を持たせることを狙ったものもある。具体的には,各タスクの最大実行時間やデッドラインなどを登録しておくと,OSが時間制約を満たすように処理のスケジューリングを行う。時間制約を満たせない場合には,処理をOSに登録しようとした時点でエラーとなる。もちろんアプリケーション・ソフトウェアは,OSに登録した最大実行時間を守って実行することが前提となる。メチャクチャな数字を登録してもリアルタイム性を確保できるわけではない。

 このアプローチは,アプリケーション設計者にとっては非常に魅力的に見えるだろう。しかし,OS自身の予測可能性は怪しくなる。複雑なスケジューリング処理が,何μs以内に終わると保証することは,容易ではないからである。またアプリケーション設計者にとっても,時間制約を満たせないということでOSがエラーを返した時に,どう対処するかが難しい。スケジューリング・アルゴリズムが複雑だと,どういう状況でエラーになるかの予測は困難である。結局,アプリケーション側も,何らかの保証を与えにくくなると思われる。研究レベルにとどまっているのは,このためであろう。

 そこで,もう少し現実的なアプローチとして,タスク(またはタスクの集合,以下同じ)に対して,プロセッサをどれだけ使用できるかを決めておき,その分だけは確実にプロセッサを使えるようにする方法が有力である。具体的には,T msごとにC ms使えるとか,プロセッサの能力のS%使えるといったように決めておく。OSは,タスクに対して,決められた分のプロセッサ時間を使えることを保障する。

 逆に,あるタスクが決められた分を超えてプロセッサを使おうとした場合には,タスクの実行を中断する(または,打ち切る)ことになる。決められたプロセッサ時間内で時間制約を満たして処理を終えるのは,アプリケーション側の責任である。

 このアプローチは,タスクがプロセッサ時間を予約すると見なせることから,CPUリザベーション,あるいはリソース・リザベーションと呼ばれることが多い。あるタスクが決められたプロセッサ時間を超えて実行しようとした場合でも,他のタスクの使うプロセッサ時間を守ることから,筆者はこのアプローチをプロセッサ時間の保護機能ととらえている。

 プロセッサ時間の保護機能を持つOSを使うと,各タスクが決められたプロセッサ時間内に時間制約を満たして処理を終えることを検証すれば,システム全体が時間制約を満たすことはOSが保障してくれる。これは,システムの時間制約に関する検証の大幅な省力化につながる。複雑なリアルタイムシステムの構築には有効性が高い。筆者は,次世代のリアルタイムOSが持つべき機能だと考えている。研究室でも,厳しいリアルタイム制約を持ったシステムをターゲットに,プロセッサ時間の保護機能を持つOSの研究を進めている。