実時間スケジューリング制約は、データ取得やプロセス制御ハードウェアの管理のために必要です。実時間環境では、プロセスが制限された時間内で外部イベントに反応する必要があります。この制約は、処理する資源をタイムシェアリングプロセスのセットに「公平に」分配するように設計されているカーネルの能力を超えることがあります。
この節では、SunOS 5.0 から 5.8 の実時間スケジューラ、その優先順位待ち行列、およびスケジューリングを制御するシステムコールとユーティリティの使用方法について説明します。
実時間アプリケーションをスケジューリングする際に最も重要な要素は、実時間スケジューリングクラスを用意することです。標準のタイムシェアリングのスケジューリングクラスは、どのプロセスも平等に扱って優先順位の概念に制限があるので、実時間アプリケーションには適しません。実時間アプリケーションでは、プロセスの優先順位が絶対的なものとして受け取られ、アプリケーションの明示的な操作によってしか変更されないスケジューリングクラスが必要です。
ディスパッチ中の潜在的な時間とは、プロセスの操作開始の要求にシステムが応答するのにかかる時間を指します。アプリケーションの優先順位を尊重するように特別に作成されたスケジューラを使用すると、ディスパッチ中の潜在的な時間を制限した実時間アプリケーションを開発できます。
図 8-2 に、アプリケーションが外部イベントの要求に応答するのにかかる時間を示します。
全体的なアプリケーションの応答時間には、割り込み応答時間、ディスパッチ中の潜在的な時間、およびアプリケーションが応答を決定するのにかかる時間が含まれます。
アプリケーションの割り込み応答時間には、システムの割り込み中の潜在的な時間とデバイスドライバの割り込み処理時間が含まれます。割り込み中の潜在的な時間は、システムが割り込みを無効にして実行しなければならない最長の間隔によって決まります。これは SunOS 5.0 から 5.8 では、プロセッサの割り込みレベルの上昇を通常は要求しない同期プリミティブを使用して最小化されています。
割り込み処理中は、ドライバの割り込みルーチンが優先順位の高いプロセスを呼び起こして終了すると戻ります。システムでは、割り込まれたプロセスよりも高い優先順位を持つプロセスが現在ディスパッチ可能であることが検知され、そのプロセスをディスパッチするように指定されます。優先順位の低いプロセスから高いプロセスへコンテキストスイッチングする時間は、ディスパッチ中の潜在的な時間に含まれます。
図 8-3 に、システムが外部イベントに応答するのにかかる時間として定義された、システムの内部ディスパッチ中の潜在的な時間とアプリケーションの応答時間を示します。内部イベントのディスパッチ中の潜在的な時間は、あるプロセスがより高い優先順位のプロセスを呼び起こし、システムでそのプロセスがディスパッチされるのにかかる時間を表します。
アプリケーションの応答時間は、ドライバがより高い優先順位のプロセスを呼び起こし、優先順位の低いプロセスに資源を解放させ、より高い優先順位のタスクを再スケジュールして応答を計算し、タスクをディスパッチするのにかかる合計時間です。
ディスパッチ中の潜在的な時間のインタバル間に割り込みが入って処理されることがあります。この処理でアプリケーションの応答時間は増えますが、ディスパッチ中の潜在的な時間の測定には影響を与えないので、ディスパッチ中の潜在的な時間の保証によって制限されることはありません。
実時間 SunOS 5.0 から 5.8 で用意されている新しいスケジューリング手法によって、システムのディスパッチ中の潜在的な時間は指定された範囲内になります。下の表に示すように、ディスパッチ中の潜在的な時間はプロセス数を制限すると改善されます。
表 8-1 SunOS 5.0 から 5.8 の実時間システムのディスパッチ中の潜在的な時間ワークステーション | 制限されたプロセス数 | 任意のプロセス数 |
---|---|---|
SPARCstationTM 2 | 有効なプロセスが 16 個未満の場合は、システム内で 0.5 ミリ秒未満 | 1.0 ミリ秒 |
SPARCstation 5 | 0.3 ミリ秒 | 0.3 ミリ秒 |
UltraTM 1-1677 | 0.15 ミリ秒未満 | 0.15 ミリ秒未満 |
ディスパッチ中の潜在的な時間の検査と、製造業務やデータ収集業務などのクリティカルな環境での経験によって、Sun ワークステーションは実時間アプリケーション開発のための有効なプラットフォームであることが証明されています。(ただし上記の例は、最新製品によるものではありませんのでご了承ください)。
SunOS 5.0 から 5.8 のカーネルは、プロセスを優先順位によってディスパッチします。スケジューラ (またはディスパッチャ) は、スケジューリングクラスの概念をサポートしています。クラスは、実時間 (RT)、システム (sys)、またはタイムシェアリング (TS) として定義されます。各クラスには、プロセスをディスパッチするための固有のスケジューリング方式があります。
カーネルは、最も優先順位が高いプロセスを最初にディスパッチします。デフォルトでは、実時間プロセスが sys や TS のプロセスよりも優先されますが、管理者は TS と RT のプロセスの優先順位が重なり合うように設定することもできます。
図 8-4 に SunOS 5.0 から 5.8 のカーネルから見たクラスの概念を示します。
最も優先順位が高いのはハードウェア割り込みで、これはソフトウェアでは制御できません。割り込みを処理するルーチンは、割り込みが生じるとただちに直接ディスパッチされ、その際には現在のプロセスの優先順位は考慮されません。
実時間プロセスは、ソフトウェアでは最も高い優先順位をデフォルトで持ちます。RT クラスのプロセスは、優先順位とタイムカンタム (time quantum) 値を持ちます。RT プロセスは、厳密にこれらのパラメタに基づいてスケジュールされます。RT プロセスが実行可能である限り、SYS や TS のプロセスは実行できません。固定優先順位スケジューリングでは、クリティカルプロセスを完了まで事前に指定した順序で実行できます。この優先順位は、アプリケーションで変更されない限り変わりません。
RT クラスのプロセスは、有限無限を問わず親プロセスのタイムカンタムを継承します。有限タイムカンタムを持つプロセスは、タイムカンタムの有効時間が切れるか、プロセスが終了するか、ブロッキングされるか (入出力イベントを待つ)、またはより高い優先順位を持つ実行可能な実時間プロセスに横取りされるまで実行されます。無限タイムカンタムを持つプロセスは、プロセスが終了するか、ブロッキングされるか、または横取りされるまで実行されます。
SYS クラスは、ページング、STREAMS、スワッパなどの特殊なシステムプロセスをスケジュールするために存在します。あるプロセスのクラスを SYS クラスに変更できません。プロセスの SYS クラスは、プロセスの開始時にカーネルによって確立された固定優先順位を持っています。
優先順位が最も低いのは、タイムシェアリング (TS) クラスです。TS クラスのプロセスは、各タイムスライスを数百ミリ秒として動的にスケジュールされます。TS スケジューラは、頻繁にコンテキストスイッチングを行なって、各プロセスに実行する機会を平等に与えます。これは、各プロセスのタイムスライスの値とプロセスの履歴 (プロセスが最後に休眠したのはいつか) に基づき、CPU の利用率を考慮して行われます。デフォルトのタイムシェアリング方式では、優先順位の低いプロセスに長いタイムスライスを与えます。
子プロセスは fork(2) を通じて、親プロセスのスケジューリングクラスと属性を継承します。プロセスのスケジューリングクラスと属性は、exec(2) を実行しても変わりません。
各スケジューリングクラスは、異なったアルゴリズムによってディスパッチされます。クラスに依存するルーチンは、カーネルによって呼び出され、CPU のプロセススケジューリングが決定されます。カーネルはクラスに依存し、待ち行列内から最も優先順位の高いプロセスを取り出します。各クラスは、自分のクラスのプロセスの優先順位値を計算しなければなりません。この値は、そのプロセスのディスパッチ優先順位変数に入れられます。
図 8-5 に示すように各クラスのアルゴリズムは、それぞれ独自の方法によってグローバル実行待ち行列に入れる最も優先順位の高いプロセスを指定します。
各クラスには、そのクラスのプロセスに適用される優先順位レベルのセットがあります。クラス固有のマッピングによって、この優先順位がグローバル優先順位のセットに割り当てられます。グローバルスケジューリング優先順位のセットへの対応は 0 で始まったり連続したりしている必要はありません。
デフォルトでは、タイムシェアリング (TS) プロセスのグローバル優先順位の値は -20 から +20 までの範囲で、カーネルの 0 から 40 までに割り当てられており、一時的な割り当ては 99 まであります。実時間 (RT) プロセスのデフォルトの優先順位は 0 から 59 までの範囲で、カーネルの 100 から 150 までに割り当てられます。カーネルのクラスに依存しないコードは、待ち行列内のグローバル優先順位の最も高いプロセスを実行します。
ディスパッチ待ち行列は、同じグローバル優先順位を持つプロセスが線状にリンクしたリストです。各プロセスは、それぞれに接続されているクラス固有の情報によって起動されます。プロセスは、グローバル優先順位に基づいたカーネルのディスパッチテーブルからディスパッチされます。
プロセスがディスパッチされると、プロセスのコンテキストがメモリ管理情報、レジスタ、スタックとともにメモリ内に割り当てられて実行が始まります。メモリ管理情報は、現在実行中のプロセスのために仮想記憶変換を実行する際、必要となるデータを含むハードウェアレジスタの形をとります。
より高い優先順位を持つプロセスがディスパッチ可能になると、カーネルは計算に割り込んでコンテキストスイッチングを強制し、現在実行中のプロセスを横取りします。より高い優先順位のプロセスがディスパッチ可能になったことをカーネルが見つけると、プロセスはいつでも横取りされます。
たとえば、プロセス A が周辺デバイスから読み取りを行なっているとします。プロセス A はカーネルによって休眠状態に置かれます。次に、カーネルはより優先順位の低いプロセス B が実行可能になったのに気づき、プロセス B がディスパッチされ実行が始まります。ここで周辺デバイスが割り込み、デバイスのドライバが入ります。デバイスドライバはプロセス A を実行可能にして戻ります。ここで、カーネルは割り込まれたプロセス B に戻るのではなく、B の処理を横取りして、呼び起こされたプロセス A の実行を再開します。
もう 1 つの重要な例としては、複数のプロセスがカーネル資源を奪い合う場合があります。優先順位の高い実時間プロセスが待っている資源を優先順位の低いプロセスが解放すると、カーネルはただちに優先順位の低いプロセスを横取りして、優先順位の高いプロセスの実行を再開します。
優先順位の反転は、優先順位の高いプロセスが 1 つまたは複数の優先順位の低いプロセスによって長時間ブロッキングされた場合に生じます。SunOS 5.0 から 5.8 のカーネルで相互排他ロッキングなどの同期プリミティブを使用すると、優先順位の反転につながることがあります。
「ブロッキング」とは、あるプロセスが 1 つまたは複数のプロセスが資源を手放すのを待たなければならない状態のことです。このブロッキングが継続すると、使用レベルが低いものでもデッドラインを逃してしまうことがあります。
相互排他ロッキングの優先順位反転の問題については、SunOS 5.0 から 5.8 のカーネルで基本的な優先順位継承方式を実装することによって対応しています。この方式では、優先順位の低いプロセスが優先順位の高いプロセスの実行をブロッキングすると、優先順位の低いプロセスが優先順位の高いプロセスの優先順位を継承することになります。このため、プロセスがブロッキングされている時間の上限が設定されます。この方式はカーネルの特性で、プログラマがシステムコールや関数の実行によって講じる解決策ではありません。ただしこの場合でも、ユーザレベルのプロセスは優先順位の反転を生じることがあります。
この問題とその対処方法については、『マルチスレッドのプログラミング』の「相互排他ロック属性」の節で説明しています。
有効なクラスのスケジューリング制御は、priocntl(2) で処理します。クラスの属性は、fork(2) や exec(2) を実行した場合にも、優先順位制御に必要なスケジューリングパラメタやアクセス権とともに継承されます。この特色は、RT と TS クラスのどちらにも当てはまります。
priocntl(2) 関数は、システムコールが適用される実時間プロセス、プロセスのセット、またはクラスを指定するインタフェースを提供します。priocntlset(2) のシステムコールも、システムコールを適用するプロセスのセット全体を指定する、さらに一般的なインタフェースを提供します。
priocntl(2) のコマンド引数は、PC_GETCID、PC_GETCLINFO、PC_GETPARMS、PC_SETPARMS のいずれかにします。呼び出しプロセスの実識別子または実効識別子は、対象となるプロセスのものと一致するか、スーパーユーザ特権を持っていなければなりません。
PC_GETCID |
このコマンドは、認識可能なクラス名 (実時間なら RT、タイムシェアリングなら TS) を含む構造体の名前フィールドを受け入れます。クラス ID とクラス属性データの配列が戻されます。 |
PC_GETCLINFO |
このコマンドは、認識可能なクラス識別子を含む構造体の ID フィールドを受け入れます。クラス名とクラス属性データの配列が戻されます。 |
PC_GETPARMS |
このコマンドは、指定したプロセスの 1 つのスケジューリングクラス識別子またはクラス固有のスケジューリングパラメタ、あるいはその両方を戻します。idtype と id によって大きなセットが指定された場合でも、PC_GETPARMS は 1 つのプロセスのパラメタだけを戻します。どのプロセスを選択するかはクラスによって決まります。 |
PC_SETPARMS |
このコマンドは、指定したプロセス (複数でも可) のスケジューリングクラス識別子またはクラス固有のスケジューリングパラメタ、あるいはその両方を設定します。 |
指定された方針の最大値を戻します。
指定された方針の最小値を戻します (詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください)。
指定された timespec 構造体を現在の実行時間限界に更新します (詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください)。
指定されたプロセスのスケジューリングパラメタを設定または取得します。
プロセスリストの先頭に戻るまで、呼び出しプロセスをブロッキングします。
プロセスのスケジューリングを制御する管理用ユーティリティとして、dispadmin() と priocntl(1) があります。どちらのユーティリティも、互換性のあるオプションとロード可能なモジュールを伴う priocntl(2) のシステムコールをサポートします。これらのユーティリティは、実行中に実時間プロセスのスケジューリングを制御するシステム管理機能を提供します。
priocntl(1) コマンドは、プロセスのスケジューリングパラメタの設定と取り出しを行います。
dispadmin() ユーティリティに -l コマンド行オプションを付けると、実行中に現プロセスのすべてのスケジューリングクラスが表示されます。実時間クラスを表す引数として RT を -c オプションの後ろに指定すると、プロセスのスケジューリングを変更することもできます。
表 8-2 に示しているオプションも使用できます。
表 8-2 dispadmin(1M) ユーティリティのクラスオプション
オプション |
意味 |
---|---|
-l |
現在設定されているスケージュリングクラスを表示する。 |
-c |
パラメタを表示または変更するクラスを指定する。 |
-g |
指定したクラスのディスパッチパラメタを取得する。 |
-r |
-g オプションと共に使用した場合、タイムカンタム (time quantum) の解像度を指定する。 |
-s |
値が保存されているファイルを指定する。 |
ディスパッチパラメタが保存されているクラス固有のファイルを実行中にロードすることもできます。このファイルを使用して、起動時に確立されたデフォルトの値を新しい優先順位のセットで置き換えることができます。このクラス固有のファイルでは、-g オプションで使用される書式の引数を挿入しなければなりません。RT クラスのパラメタは rt_dptbl(4) にあり、この節の終わりに例を示します。
システムに RT クラスのファイルを追加するには、次のモジュールが存在しなければなりません。
rt_dptbl(4) をロードするクラスモジュール内の rt_init() ルーチン
ディスパッチパラメタと、config_rt_dptbl へのポインタを戻すルーチンを提供する rt_dptbl(4)
dispadmin() 実行可能ファイル
次のコマンドでクラス固有のモジュールをロードします。
この場合、module_name はクラス固有のモジュールを指定します。
# modload /kernel/sched/module_name |
dispadmin() コマンドを起動します。
# dispadmin -c RT -s file_name |
上書きされるテーブルと同じ数のエントリを持つテーブルが、ファイルに記述されていなければなりません。
両方のスケジューリングクラスにはパラメタテーブル rt_dptbl(4) と ts_dptbl(4) が関連づけられています。これらのテーブルは、起動時にロード可能なモジュールを使用するか、実行中に dispadmin() を使用して設定できます。
実時間のための中心となるテーブルで、RT スケジューリングの設定項目を指定します。rt_dptbl(4) 構造体は、パラメタ配列の structrt_dpent_t 構造体から成り、これは n 個の優先順位レベルそれぞれに 1 つずつあります。ある優先順位レベルの設定項目は、配列内の i 番目のパラメタ構造体 rt_dptbl[i] によって指定されます。
パラメタ構造体は次のメンバーから成ります (/usr/include/sys/rt.h ヘッダファイルにも記述されています)。
rt_globpri |
この優先順位レベルに関係づけられているグローバルスケジューリング優先順位。rt_globpri の値は dispadmin() では変更できません。 |
rt_quantum |
このレベルのプロセスに割り当てられるタイムカンタムの長さを目盛で表したもの (「タイムスタンプ機能」を参照)。タイムカンタム値は、特定のレベルのプロセスのデフォルト値、つまり開始値です。実時間プロセスのタイムカンタムは、priocntl(1) コマンドまたは priocntl(2) システムコールによって変更できます。 |
実時間管理者は、いつでも config_rt_dptbl を再設定して、スケジューラの実時間部分の動作を変更できます。1 つの方法は、rt_dptbl(4) のマニュアルページの「REPLACING THE RT_DPTBL LOADABLE MODULE」の節で説明されています。
もう 1 つの方法は、dispadmin() コマンドを使用して、実行中のシステムで実時間パラメタテーブルを調査または変更する方法です。dispadmin() を実時間クラスで起動すると、カーネルの中心テーブルにある現在の config_rt_dptb1 内から現在の rt_quantum 値を取り出すことができます。現在の中心テーブルを上書きする際、dispadmin() への入力に使用された設定ファイルは、rt_dptbl(4) のマニュアルページで説明されている書式に合致していなければなりません。
config_rt_dptbl[] 内にある優先順位を設定されたプロセス rtdpent_t と、関連づけられているタイムカンタム値を次に示します。
rtdpent_t rt_dptbl[] = { 129, 60, /* 優先順位レベルのタイムカンタム */ 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, } |