リアルタイムスケジューリング制約は、データ取得やプロセス制御ハードウェアの管理のために必要です。リアルタイム環境では、プロセスが制限された時間内で外部イベントに反応する必要があります。この制約は、処理する資源をタイムシェアリングプロセスのセットに公平に分配するように設計されているカーネルの能力を超えることがあります。
この節では、SunOS のリアルタイムスケジューラ、その優先順位待ち行列、およびスケジューリングを制御するシステムコールとユーティリティーの使用方法について説明します。
リアルタイムアプリケーションをスケジューリングする際にもっとも重要な要素は、リアルタイムスケジューリングクラスを用意することです。標準のタイムシェアリングのスケジューリングクラスはどのプロセスも平等に扱い、優先順位の概念に制限があります。したがって、リアルタイムアプリケーションには適しません。リアルタイムアプリケーションは、プロセスの優先順位が絶対的なものとして受け取られるスケジューリングクラスを必要とします。リアルタイムアプリケーションはまた、プロセスの優先順位がアプリケーションの明示的な操作でしか変更されないスケジューリングクラスを必要とします。
「ディスパッチ応答時間」とは、プロセスの操作開始の要求にシステムが応答するまでの時間を指します。アプリケーションの優先順位を尊重するように特別に作成されたスケジューラを使用すると、ディスパッチ応答時間を制限したリアルタイムアプリケーションを開発できます。
次の図に、あるアプリケーションが外部イベントからの要求に応答するまでの時間を示します。
全体のアプリケーション応答時間には、割り込み応答時間、ディスパッチ応答時間、およびアプリケーションの応答時間が含まれます。
アプリケーションの割り込み応答時間には、システムの割り込み応答時間とデバイスドライバ独自の割り込み処理時間が含まれます。割り込み応答時間は、システムが割り込みを無効にして実行する必要がある最長の間隔によって決まります。SunOS では、この時間を最小限にするために、通常はプロセッサの割り込みレベルを上げる必要がない同期プリミティブを使用しています。
割り込み処理中、ドライバの割り込みルーチンはまず、優先順位が高いプロセスを呼び起こし、そのプロセスが終了すると戻ります。割り込まれたプロセスよりも優先順位が高いプロセスが現在ディスパッチ可能であることを検出すると、システムは (優先順位が高い) プロセスをディスパッチします。優先順位が低いプロセスから高いプロセスへコンテキストを切り換える時間は、ディスパッチ応答時間に含まれます。
図 12–3 に、システムの内部イベントのディスパッチ応答時間とアプリケーション応答時間を示します。アプリケーション応答時間は、システムが内部イベントに応答するまでに必要な時間のことです。内部イベントのディスパッチ応答時間とは、あるプロセスが優先順位がより高いプロセスを呼び起こすまでに必要な時間のことです。このディスパッチ応答時間には、システムが優先順位がより高いプロセスをディスパッチするまでに必要な時間も含まれます。
アプリケーション応答時間とは、ドライバが次の作業を完了するまでに必要な時間のことです。 つまり、優先順位がより高いプロセスを呼び起こし、優先順位が低いプロセスから資源を解放し、優先順位がより高いタスクをスケジューリングし直し、応答を計算し、タスクをディスパッチすることです。
ディスパッチ応答時間のインターバルの間に割り込みが入って処理されることがあります。この処理でアプリケーション応答時間は増えますが、ディスパッチ応答時間の測定には影響を与えません。したがって、この処理はディスパッチ応答時間の保証には制限されません。
リアルタイム SunOS に用意されている新しいスケジューリング手法を使用すると、システムのディスパッチ応答時間を指定された範囲に限定できます。次の表に示すように、プロセス数を制限するとディスパッチ応答時間が改善されます。
表 12–1 リアルタイムシステムディスパッチ応答時間
ワークステーション |
制限されたプロセス数 |
任意のプロセス数 |
---|---|---|
SPARCstation 2 |
動作中のプロセスが 16 個未満の場合は、システム内で 0.5 ミリ秒未満 |
1.0 ミリ秒 |
SPARCstation 5 |
0.3 ミリ秒未満 |
0.3 ミリ秒 |
SunOS のカーネルは、プロセスを優先順位によってディスパッチします。スケジューラ (またはディスパッチャー) は、スケジューリングクラスの概念をサポートしています。クラスは、リアルタイム (RT)、システム (sys)、およびタイムシェアリング (TS) として定義されます。各クラスには、プロセスをディスパッチするための固有のスケジューリング方式があります。
カーネルは、もっとも優先順位が高いプロセスを最初にディスパッチします。デフォルトでは、リアルタイムプロセスが sys や TS のプロセスよりも優先されます。システム管理者は、TS と RT のプロセスの優先順位が重なり合うように設定することもできます。
次の図に、SunOS カーネルから見たクラスの概念を示します。
ハードウェア割り込みは優先順位がもっとも高いので、ソフトウェアでは制御できません。割り込みを処理するルーチンは、割り込みが生じるとただちに直接ディスパッチされ、その際には現在のプロセスの優先順位は考慮されません。
リアルタイムプロセス (RT) は、ソフトウェアではもっとも高い優先順位をデフォルトで持ちます。RT クラスのプロセスは、優先順位とタイムカンタム (time quantum) 値を持ちます。RT プロセスは、厳密にこれらのパラメータに基づいてスケジューリングされます。RT プロセスが実行可能である限り、SYS や TS のプロセスは実行できません。固定優先順位スケジューリングでは、クリティカルプロセスを完了まで事前に指定された順序で実行できます。この優先順位は、アプリケーションで変更されない限り変わりません。
RT クラスのプロセスは、有限無限を問わず親プロセスのタイムカンタムを継承します。有限タイムカンタムを持つプロセスは、タイムカンタムの有効期限が切れるまで実行されます。有限タイムカンタムを持つプロセスはまた、入出力イベントを待つ間ブロックされるか、またはより高い優先順位を持つ実行可能なリアルタイムプロセスに横取りされるまで実行されます。無限タイムカンタムを持つプロセスは、プロセスが終了するか、ブロックされるか、または横取りされるまで実行されます。
SYS クラスは、ページング、STREAMS、スワッピングなどの特殊なシステムプロセスをスケジューリングするために存在します。通常のプロセスのクラスは SYS クラスには変更できません。プロセスの SYS クラスは、プロセスの開始時にカーネルによって確立された固定優先順位を持っています。
優先順位がもっとも低いのは、タイムシェアリング (TS) クラスです。TS クラスのプロセスは、各タイムスライスを数百ミリ秒として動的にスケジューリングされます。TS スケジューラは、次の値に基づいて、ラウンドロビン方式でコンテキストを切り換えることによって、すべてのプロセスに平等な機会を提供します。
タイムスライス値
プロセス履歴 (プロセスが最後に休眠状態に入ったときを記録する)
CPU 使用率
デフォルトのタイムシェアリング方式では、優先順位が低いプロセスに長いタイムスライスが与えられます。
子プロセスは fork(2) を通じて、親プロセスのスケジューリングクラスと属性を継承します。exec(2) を実行しても、プロセスのスケジューリングクラスと属性は変わりません。
各スケジューリングクラスは、異なったアルゴリズムによってディスパッチされます。クラスに依存するルーチンはカーネルによって呼び出され、CPU のプロセススケジューリングが決定されます。カーネルはクラスから独立しており、優先順位がもっとも高いプロセスを待ち行列内から取り出します。各クラスは、自分のクラスのプロセスの優先順位値を計算しなければなりません。この値は、そのプロセスのディスパッチ優先順位変数に入れられます。
次の図に示すように、各クラスのアルゴリズムは独自の方法で優先順位がもっとも高いプロセスを選択して、グローバル実行待ち行列に入れます。
各クラスには、そのクラスのプロセスに適用される優先順位レベルのセットがあります。クラス固有のマッピングによって、この優先順位がグローバル優先順位のセットに割り当てられます。グローバルスケジューリング優先順位マッピングセットは、0 で始まっていたり、連続したりしている必要はありません。
デフォルトでは、タイムシェアリング (TS) プロセスのグローバル優先順位の値は -20 から +20 までの範囲です。このようなグローバル優先順位の値はカーネルの 0 から 40 までに割り当てられており、一時的な割り当ては 99 まであります。リアルタイム (RT) プロセスのデフォルトの優先順位は 0 から 59 までの範囲で、カーネルの 100 から 159 までに割り当てられます。カーネルのクラスに依存しないコードは、待ち行列内のグローバル優先順位のもっとも高いプロセスを実行します。
ディスパッチ待ち行列は、同じグローバル優先順位を持つプロセスが直線的にリンクしたリストです。各プロセスには起動時に、クラス固有な情報が付けられます。プロセスは、グローバル優先順位に基づく順番で、カーネルのディスパッチテーブルからディスパッチされます。
プロセスがディスパッチされると、メモリー管理情報、レジスタ、スタックとともに、プロセスのコンテキストがメモリー内に割り当てられます。コンテキストマッピングが完了したあと、実行が始まります。メモリー管理情報はハードウェアレジスタの形式をしており、現在実行中のプロセスのために仮想メモリー変換を実行するときに必要となるデータが入っています。
より高い優先順位を持つプロセスがディスパッチ可能になると、カーネルはコンピュータ操作に割り込んで強制的にコンテキストを切り換え、現在実行中のプロセスを横取りします。より高い優先順位のプロセスがディスパッチ可能になったことをカーネルが見つけると、プロセスはいつでも横取りされます。
たとえば、プロセス A が周辺デバイスから読み取りを行なっているとします。プロセス A はカーネルによって休眠状態に置かれます。次に、カーネルはより優先順位の低いプロセス B が実行可能になったのに気づきます。すると、プロセス B がディスパッチされ、実行が始まります。ここで周辺デバイスが割り込みを送信し、デバイスドライバの処理に入ります。デバイスドライバはプロセス A を実行可能にして戻ります。ここで、カーネルは割り込まれたプロセス B に戻るのではなく、B の処理を横取りして、呼び起こされたプロセス A の実行を再開します。
もう 1 つの重要な例としては、複数のプロセスがカーネル資源を争奪する場合があります。たとえば、優先順位の高いリアルタイムプロセスが優先順位の低いプロセスが持っている資源を待っていると仮定します。このとき、優先順位の低いプロセスがその資源を解放すると、カーネルは優先順位の低いプロセスを横取りして、優先順位の高いプロセスの実行を再開します。
優先順位の逆転は、優先順位の高いプロセスが 1 つまたは複数の優先順位の低いプロセスによって長時間ブロックされた場合に生じます。SunOS のカーネルで相互排他ロックなどの同期プリミティブを使用すると、優先順位の逆転につながることがあります。
「ブロック化」とは、あるプロセスが 1 つまたは複数のプロセスが資源を手放すのを待たなければならない状態のことです。このブロック化が継続すると、使用レベルが低いものでもデッドラインに間に合わないことがあります。
相互排他ロックによって優先順位が逆転する問題については、SunOS のカーネルで基本的な優先順位継承方式を実装することによって対応しています。この方式では、優先順位の低いプロセスが優先順位の高いプロセスの実行をブロックすると、優先順位の低いプロセスが優先順位の高いプロセスの優先順位を継承することになります。この継承のため、プロセスがブロック化されている時間の上限が設定されます。この方式はカーネルの動作であり、プログラマがシステムコールやインタフェースの実行によって講じる解決策ではありません。ただし、この場合でもユーザーレベルのプロセスは優先順位の逆転を生じることがあります。
ユーザー優先順位が逆転する問題とその対処方法については、『マルチスレッドのプログラミング』の「相互排他ロック属性」の節を参照してください。
次に、プロセスのスケジューリングを制御するインタフェース呼び出しを説明します。
動作中のクラスのスケジューリング制御は、priocntl(2) で処理します。fork(2) や exec(2) を実行した場合、クラスの属性は、優先順位の制御に必要なスケジューリングパラメータやアクセス権とともに継承されます。この継承は、RT クラスと TS クラスの両方に当てはまります。
priocntl(2) は、システムコールが適用されるリアルタイムプロセス、プロセスのセット、またはクラスを指定するインタフェースです。priocntlset(2) も、システムコールを適用するプロセスのセット全体を指定する、より一般的なインタフェースを提供します。
priocntl(2) のコマンド引数は、PC_GETCID、PC_GETCLINFO、PC_GETPARMS、PC_SETPARMS のいずれかにします。呼び出し側プロセスの実 ID または実効 ID は、対象となるプロセスの実 ID または実効 ID と一致するか、あるいは、スーパーユーザー特権を持つ必要があります。
このコマンドは、認識可能なクラス名を含む構造体の名前フィールドを受け入れます。クラス ID とクラス属性データの配列が返されます。
このコマンドは、認識可能なクラス識別子を含む構造体の ID フィールドを受け入れます。クラス名とクラス属性データの配列が返されます。
このコマンドは、指定されたプロセスのうち、1 つのプロセスのスケジューリングクラス識別子またはクラス固有のスケジューリングパラメータを返します。idtype と id によって大きなセットが指定された場合でも、PC_GETPARMS は 1 つのプロセスのパラメータだけを返します。どのプロセスを選択するかはクラスによって決まります。
このコマンドは、指定されたプロセス (複数可) のスケジューリングクラスまたはクラス固有のスケジューリングパラメータを設定します。
指定された方針の最大値を返します。
指定された方針の最小値を返します。詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください。
指定された timespec 構造体を現在の実行時間制限に更新します。詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください。
指定されたプロセスのスケジューリングパラメータを設定または取得します。
呼び出し側プロセスがプロセスリストの先頭に戻るまで、呼び出し側プロセスをブロックします。
プロセスのスケジューリングを制御するシステム管理用ユーティリティーには、dispadmin(1M) および priocntl(1) があります。どちらのユーティリティーも priocntl(2) システムコールをサポートし、オプションに互換性があり、モジュールもロード可能です。これらのユーティリティーは、実行時にリアルタイムプロセスのスケジューリングを制御するシステム管理機能を提供します。
priocntl(1) コマンドは、プロセスのスケジューリングパラメータの設定および取得を行います。
dispadmin(1M) ユーティリティーに -l コマンド行オプションを付けると、実行時に現在のプロセスのすべてのスケジューリングクラスが表示されます。リアルタイムクラスを表す引数として RT を -c オプションの後ろに指定すると、プロセスのスケジューリングを変更することもできます。
dispadmin(1M) のクラスオプションは次のとおりです。
現在設定されているスケージュリングクラスを表示する
パラメータを表示または変更するクラスを指定する
指定されたクラスのディスパッチパラメータを取得する
-g オプションとともに使用し、タイムカンタムの精度を指定する
値が保存されているファイルを指定する
ディスパッチパラメータが保存されているクラス固有のファイルは実行時にもロードできます。このファイルを使用して、ブート時に確立されたデフォルトの値を新しい優先順位のセットで置き換えることができます。このクラス固有のファイルは、-g オプションで使用される書式で引数を表明する必要があります。RT クラスのパラメータについては rt_dptbl(4) を参照し、例については、例 12–1 を参照してください。
システムに RT クラスのファイルを追加するには、次のモジュールが存在する必要があります。
rt_dptbl(4) をロードするクラスモジュール内の rt_init() ルーチン
ディスパッチパラメータと config_rt_dptbl へのポインタを返すルーチンを提供する rt_dptbl(4) モジュール
dispadmin(1M) 実行可能モジュール
次の手順で、RT クラスのディスパッチテーブルのインストールを行います。
次のコマンドでクラス固有のモジュールをロードする。module_name にはクラス固有のモジュールを指定する。
# modload /kernel/sched/module_name |
dispadmin コマンドを起動する。
# dispadmin -c RT -s file_name |
上書きされるテーブルと同じ数のエントリを持つテーブルが、ファイルに記述されている必要があります。
両方のスケジューリングクラスには、パラメータテーブル rt_dptbl(4) と ts_dptbl(4) が関連付けられています。これらのテーブルは、ブート時にロード可能なモジュールを使用するか、実行時に dispadmin(1M) を使用することで設定されます。
リアルタイムのための中心となるテーブルで、RT スケジューリングの設定項目を指定します。rt_dptbl(4) 構造体はパラメータの配列 struct rt_dpent_t から構成されます。n 個の優先順位レベルはそれぞれ 1 つのパラメータを持っています。ある優先順位レベルの設定項目は、配列内の i 番目のパラメータ構造体 rt_dptbl[i] によって指定されます。
パラメータ構造体は次のメンバーから構成されます (これらは、/usr/include/sys/rt.h ヘッダファイルでも説明されています)。
この優先順位レベルに関係付けられているグローバルスケジューリング優先順位。dispadmin(1M) では rt_globpri の値は変更できない
このレベルのプロセスに割り当てられるタイムカンタムの長さを目盛で表したもの。詳細は、「タイムスタンプインタフェース」を参照してください。タイムカンタム値は、特定のレベルのプロセスのデフォルト値、つまり開始値。リアルタイムプロセスのタイムカウンタを変更するには、priocntl(1) コマンドまたは priocntl(2) システムコールを使用する
リアルタイムのシステム管理者は、いつでも config_rt_dptbl を再構成して、スケジューラのリアルタイム部分の動作を変更できます。1 つの方法は、rt_dptbl(4) のマニュアルページの「Replacing the rt_dptbl Loadable Module」という節で説明されています。
もう 1 つの方法は、dispadmin(1M) コマンドを使用して、実行中のシステムでリアルタイムパラーメタテーブルを調査または変更する方法です。dispadmin(1M) をリアルタイムクラスで起動すると、カーネルの中心テーブルにある現在の config_rt_dptbl 内から現在の rt_quantum 値を取り出すことができます。現在の中心テーブルを上書きするとき、dispadmin(1M) への入力に使用される設定ファイルは rt_dptbl(4) のマニュアルページで説明されている書式に準拠する必要があります。
次に、config_rt_dptbl[] に表示されるプロセスとして、優先順位を設定されたプロセス rtdpent_t とそれに関連付けられたタイムカンタムの config_rt_dptbl[] 値の例を示します。
rtdpent_t rt_dptbl[] = { 129, 60, /* prilevel Time quantum */ 130, 40, 100, 100, 131, 40, 101, 100, 132, 40, 102, 100, 133, 40, 103, 100, 134, 40, 104, 100, 135, 40, 105, 100, 136, 40, 106, 100, 137, 40, 107, 100, 138, 40 108, 100, 139, 40, 109, 100, 140, 20, 110, 80, 141, 20, 111, 80, 142, 20, 112, 80, 143, 20, 113, 80, 144, 20, 114, 80, 145, 20, 115, 80, 146, 20, 116, 80, 147, 20, 117, 80, 148, 20, 118, 80, 149, 20, 119, 80, 150, 10, 120, 60, 151, 10, 121, 60, 152, 10, 122, 60, 153, 10, 123, 60, 154, 10, 124, 60, 155, 10, 125, 60, 156, 10, 126, 60, 157, 10, 126, 60, 158, 10, 127, 60, 159, 10, 128, 60, }