プログラミングインタフェース

第 12 章 リアルタイムプログラミングと管理

この章では、SunOS で実行するリアルタイムアプリケーションの書き方と移植方法について説明します。この章は、リアルタイムアプリケーションを書いた経験があるプログラマや、リアルタイム処理と Solaris システムに詳しい管理者を対象として書かれています。

この章では、次の内容について説明します。

リアルタイムアプリケーションの基本的な規則

リアルタイム応答は、一定の条件を満たした場合に保証されます。この節では、その条件を明確にし、設計上の重大なエラーをいくつか説明します。

ここでは、システムの応答時間を遅くする可能性のある問題を取り上げます。その中にはワークステーションが動かなくなるものもあります。それほど重大ではないエラーには、優先順位の逆転やシステムの過負荷などがあります。

Solaris のリアルタイムプロセスには、次のような特長があります。

この章では、リアルタイム処理を単一スレッド化プロセスとして説明していますが、マルチスレッド化プロセスにも当てはまります。マルチスレッド化プロセスについての詳細は、『マルチスレッドのプログラミング』を参照してください。スレッドのリアルタイムスケジューリングを保証するには、スレッドはバインドされたスレッドとして作成される必要があります。さらに、スレッドの LWP は RT (リアルタイム) スケジューリングクラスで実行される必要があります。メモリーのロックと初期の動的バインドは、プロセス内のすべてのスレッドについて有効です。

あるプロセスがもっとも高い優先順位を持つとき、このプロセスは、ディスパッチ応答時間内にプロセッサを取得して実行できることが保証されます。詳細は、「ディスパッチ応答時間」を参照してください。優先順位がもっとも高い実行可能なプロセスである限り、このプロセスは実行を継続します。

リアルタイムプロセッサは、システム上のほかのイベントのために、プロセッサの制御を失うことがあります。リアルタイムプロセッサはまた、システム上のほかのイベントのために、プロセッサの制御を取得できないこともあります。例として、割り込みなどの外部イベント、資源不足、同期入出力などの外部イベント待ち、より優先順位が高いプロセスによる横取りなどのイベントが挙げられます。

リアルタイムスケジューリングは通常、open(2)close(2) など、システムの初期化と終了を行うサービスには適用されません。

応答時間を低下させる要因

この節で説明する問題は、程度は異なりますが、どれもシステムの応答時間を低下させます。応答時間の低下が大きいと、アプリケーションがクリティカルなデッドラインに間に合わないことがあります。

リアルタイム処理は、システム上でリアルタイムアプリケーションを実行しているほかの有効なアプリケーションの操作を損なうこともあります。リアルタイムプロセスの優先順位は高いため、タイムシェアリングプロセスはかなりの時間、実行を妨げられます。この状況では、表示やキーボードの応答時間など、対話型の動作性が極端に低下することがあります。

同期入出力呼び出し

SunOS のシステムの応答が、入出力イベントのタイミングを制限することはありません。これは、実行がタイムクリティカルなプログラムセグメントには、同期入出力呼び出しを入れてはいけないということを意味します。時間制限が非常に長いプログラムセグメントでも、同期入出力は行わないでください。たとえば、大量の記憶領域の入出力の際に読み取りや書き込み操作を行うと、その間システムはハングアップしてしまいます。

よくあるアプリケーションの誤りは、エラーメッセージのテキストをディスクから取得するときに、入出力を実行することです。エラーメッセージのテキストをディスクから取得するには、独立したプロセスまたはスレッドから入出力を実行する必要があります。また、この独立したプロセスまたはスレッドはリアルタイムで動作していないものにしてください。

割り込みサービス

割り込みの優先順位は、プロセスの優先順位に左右されません。あるプロセスのアクションが原因で発生したハードウェア割り込みサービスは、そのプロセスのグループに設定されている優先順位を継承しません。したがって、優先順位が高いリアルタイムプロセスを制御しているデバイスに必ずしも、優先順位が高い割り込み処理が割り当てられるとは限りません。

共有ライブラリ

タイムシェアリングプロセスでは、動的にリンクされる共有ライブラリを使用すると、メモリー量を大幅に節約できます。このようなタイプのリンクは、ファイルマッピングの形で実装されます。動的にリンクされたライブラリルーチンは、暗黙の読み取りを行います。

リアルタイムプログラムは、プログラムを起動するときに、環境変数 LD_BIND_NOWNULL 以外の値を設定できます。環境変数 LD_BIND_NOW に NULL 以外の値を設定すると、共有ライブラリを使用しても動的バインドは行われません。また、すべての動的リンクは、プログラムの実行前にバインドが行なわれます。詳細は、『リンカーとライブラリ』を参照してください。

優先順位の逆転

リアルタイムプロセスが必要とする資源を、タイムシェアリングプロセスが取得すると、リアルタイムプロセスをブロックできます。優先順位の逆転は、優先順位が高いプロセスが優先順位が低いプロセスによってブロックされることで起こります。「ブロック化」とは、あるプロセスが、1 つまたは複数のプロセスが資源の制御を手放すのを待たなければならない状態のことです。ブロック化に時間がかかると、リアルタイムプロセスはデッドラインに間に合わないことがあります。

次の図に、優先順位が高いプロセスが共有資源を要求する例を示します。優先順位が低いプロセスが保持している共有資源を優先順位が中間のプロセスが横取りしているので、優先順位が高いプロセスはブロック化されています。中間のプロセスは、いくつ関与していてもかまいません。優先順位が中間のプロセスはすべて、優先順位が低いプロセスのクリティカルな部分と同様に、実行を終了する必要があります。すべての実行が終了するまでには、しばらく時間がかかることがあります。

図 12–1 制限されない優先順位の逆転

この図は、制限されない優先順位の逆転を示しています。

この問題とその対処方法については、『マルチスレッドのプログラミング』「相互排他ロック属性」の節で説明しています。

スティッキロック

ページのロックカウントが 65535 (0xFFFF) に達すると、そのページは永久にロックされます。値 0xFFFF は実装によって定義されており、将来のリリースで変更される可能性があります。このようにしてロックされたページのロックは解除できません。

ランナウェイリアルタイムプロセス

ランナウェイリアルタイムプロセスは、システムを停止させることがあります。ランナウェイリアルタイムプロセスはまた、システムが停止したように見えるほどシステムの応答を遅くしたりすることもあります。


注 –

SPARC システム上にランナウェイプロセスがある場合は、Stop-A を押します。 Stop-A は何回も押す必要があることもあります。Stop-A を押してもランナウェイプロセスが停止しない場合、電源を切ってからしばらく待ち、もう一度電源を入れ直してください。ランナウェイプロセスが SPARC 以外のシステム上にある場合も、電源を切ってからしばらく待ち、もう一度電源を入れ直してください。


優先順位が高いリアルタイムプロセスが CPU の制御を放棄しない場合、無限ループを強制的に終了させないと、システムの制御は得られません。このようなランナウェイプロセスは、Control-C を入力しても応答しません。ランナウェイプロセスよりも高い優先順位が設定されているシェルを使用しようとしても失敗します。

非同期入出力の動作

非同期入出力操作は必ずしも、カーネルの待ち行列に入った順番で実行されるとは限りません。非同期入出力操作はまた、実行された順序で呼び出し側に返されるとも限りません。

aioread(3AIO) を繰り返して高速に呼び出すことができるように単一のバッファーを指定している場合、バッファーの状態は確定されません。バッファーの状態が確定されないのは、最初の呼び出しが行われてから最後の呼び出しの結果が呼び出し側にシグナル送信されるまでの間です。

個々の aio_result_t 構造体は一度に 1 つの非同期操作だけに使用できます。この非同期操作は読み取りでも書き込みでもかまいません。

リアルタイムファイル

SunOS には、ファイルを確実に物理的に連続して割り当てる機能は用意されていません。

通常のファイルについては、read(2)write(2) の操作が常にバッファリングされます。アプリケーションは mmap(2) および msync(3C) を使用して、二次記憶領域とプロセスメモリー間の入出力転送を直接実行できます。

リアルタイムスケジューラ

リアルタイムスケジューリング制約は、データ取得やプロセス制御ハードウェアの管理のために必要です。リアルタイム環境では、プロセスが制限された時間内で外部イベントに反応する必要があります。この制約は、処理する資源をタイムシェアリングプロセスのセットに公平に分配するように設計されているカーネルの能力を超えることがあります。

この節では、SunOS のリアルタイムスケジューラ、その優先順位待ち行列、およびスケジューリングを制御するシステムコールとユーティリティーの使用方法について説明します。

ディスパッチ応答時間

リアルタイムアプリケーションをスケジューリングする際にもっとも重要な要素は、リアルタイムスケジューリングクラスを用意することです。標準のタイムシェアリングのスケジューリングクラスはどのプロセスも平等に扱い、優先順位の概念に制限があります。したがって、リアルタイムアプリケーションには適しません。リアルタイムアプリケーションは、プロセスの優先順位が絶対的なものとして受け取られるスケジューリングクラスを必要とします。リアルタイムアプリケーションはまた、プロセスの優先順位がアプリケーションの明示的な操作でしか変更されないスケジューリングクラスを必要とします。

ディスパッチ応答時間」とは、プロセスの操作開始の要求にシステムが応答するまでの時間を指します。アプリケーションの優先順位を尊重するように特別に作成されたスケジューラを使用すると、ディスパッチ応答時間を制限したリアルタイムアプリケーションを開発できます。

次の図に、あるアプリケーションが外部イベントからの要求に応答するまでの時間を示します。

図 12–2 アプリケーション応答時間

この図は、制限されない優先順位の逆転を示しています。

全体のアプリケーション応答時間には、割り込み応答時間、ディスパッチ応答時間、およびアプリケーションの応答時間が含まれます。

アプリケーションの割り込み応答時間には、システムの割り込み応答時間とデバイスドライバ独自の割り込み処理時間が含まれます。割り込み応答時間は、システムが割り込みを無効にして実行する必要がある最長の間隔によって決まります。SunOS では、この時間を最小限にするために、通常はプロセッサの割り込みレベルを上げる必要がない同期プリミティブを使用しています。

割り込み処理中、ドライバの割り込みルーチンはまず、優先順位が高いプロセスを呼び起こし、そのプロセスが終了すると戻ります。割り込まれたプロセスよりも優先順位が高いプロセスが現在ディスパッチ可能であることを検出すると、システムは (優先順位が高い) プロセスをディスパッチします。優先順位が低いプロセスから高いプロセスへコンテキストを切り換える時間は、ディスパッチ応答時間に含まれます。

図 12–3 に、システムの内部イベントのディスパッチ応答時間とアプリケーション応答時間を示します。アプリケーション応答時間は、システムが内部イベントに応答するまでに必要な時間のことです。内部イベントのディスパッチ応答時間とは、あるプロセスが優先順位がより高いプロセスを呼び起こすまでに必要な時間のことです。このディスパッチ応答時間には、システムが優先順位がより高いプロセスをディスパッチするまでに必要な時間も含まれます。

アプリケーション応答時間とは、ドライバが次の作業を完了するまでに必要な時間のことです。 つまり、優先順位がより高いプロセスを呼び起こし、優先順位が低いプロセスから資源を解放し、優先順位がより高いタスクをスケジューリングし直し、応答を計算し、タスクをディスパッチすることです。

ディスパッチ応答時間のインターバルの間に割り込みが入って処理されることがあります。この処理でアプリケーション応答時間は増えますが、ディスパッチ応答時間の測定には影響を与えません。したがって、この処理はディスパッチ応答時間の保証には制限されません。

図 12–3 内部ディスパッチ応答時間

この図は、内部ディスパッチ応答時間のコンポーネント、 呼び起こしとディスパッチを示します。

リアルタイム SunOS に用意されている新しいスケジューリング手法を使用すると、システムのディスパッチ応答時間を指定された範囲に限定できます。次の表に示すように、プロセス数を制限するとディスパッチ応答時間が改善されます。

表 12–1 リアルタイムシステムディスパッチ応答時間

ワークステーション 

制限されたプロセス数 

任意のプロセス数 

SPARCstation 2 

動作中のプロセスが 16 個未満の場合は、システム内で 0.5 ミリ秒未満 

1.0 ミリ秒 

SPARCstation 5 

0.3 ミリ秒未満 

0.3 ミリ秒 

スケジューリングクラス

SunOS のカーネルは、プロセスを優先順位によってディスパッチします。スケジューラ (またはディスパッチャー) は、スケジューリングクラスの概念をサポートしています。クラスは、リアルタイム (RT)、システム (sys)、およびタイムシェアリング (TS) として定義されます。各クラスには、プロセスをディスパッチするための固有のスケジューリング方式があります。

カーネルは、もっとも優先順位が高いプロセスを最初にディスパッチします。デフォルトでは、リアルタイムプロセスが sysTS のプロセスよりも優先されます。システム管理者は、TSRT のプロセスの優先順位が重なり合うように設定することもできます。

次の図に、SunOS カーネルから見たクラスの概念を示します。

図 12–4 スケジューリングクラスのディスパッチ優先順位

この図は、リアルタイム、カーネル、およびタイムシェアリングのプロセスからのソフトウェア割り込みよりも優先順位が高いハードウェアシステム割り込みを示します。

ハードウェア割り込みは優先順位がもっとも高いので、ソフトウェアでは制御できません。割り込みを処理するルーチンは、割り込みが生じるとただちに直接ディスパッチされ、その際には現在のプロセスの優先順位は考慮されません。

リアルタイムプロセス (RT) は、ソフトウェアではもっとも高い優先順位をデフォルトで持ちます。RT クラスのプロセスは、優先順位とタイムカンタム (time quantum) 値を持ちます。RT プロセスは、厳密にこれらのパラメータに基づいてスケジューリングされます。RT プロセスが実行可能である限り、SYSTS のプロセスは実行できません。固定優先順位スケジューリングでは、クリティカルプロセスを完了まで事前に指定された順序で実行できます。この優先順位は、アプリケーションで変更されない限り変わりません。

RT クラスのプロセスは、有限無限を問わず親プロセスのタイムカンタムを継承します。有限タイムカンタムを持つプロセスは、タイムカンタムの有効期限が切れるまで実行されます。有限タイムカンタムを持つプロセスはまた、入出力イベントを待つ間ブロックされるか、またはより高い優先順位を持つ実行可能なリアルタイムプロセスに横取りされるまで実行されます。無限タイムカンタムを持つプロセスは、プロセスが終了するか、ブロックされるか、または横取りされるまで実行されます。

SYS クラスは、ページング、STREAMS、スワッピングなどの特殊なシステムプロセスをスケジューリングするために存在します。通常のプロセスのクラスは SYS クラスには変更できません。プロセスの SYS クラスは、プロセスの開始時にカーネルによって確立された固定優先順位を持っています。

優先順位がもっとも低いのは、タイムシェアリング (TS) クラスです。TS クラスのプロセスは、各タイムスライスを数百ミリ秒として動的にスケジューリングされます。TS スケジューラは、次の値に基づいて、ラウンドロビン方式でコンテキストを切り換えることによって、すべてのプロセスに平等な機会を提供します。

デフォルトのタイムシェアリング方式では、優先順位が低いプロセスに長いタイムスライスが与えられます。

子プロセスは fork(2) を通じて、親プロセスのスケジューリングクラスと属性を継承します。exec(2) を実行しても、プロセスのスケジューリングクラスと属性は変わりません。

各スケジューリングクラスは、異なったアルゴリズムによってディスパッチされます。クラスに依存するルーチンはカーネルによって呼び出され、CPU のプロセススケジューリングが決定されます。カーネルはクラスから独立しており、優先順位がもっとも高いプロセスを待ち行列内から取り出します。各クラスは、自分のクラスのプロセスの優先順位値を計算しなければなりません。この値は、そのプロセスのディスパッチ優先順位変数に入れられます。

次の図に示すように、各クラスのアルゴリズムは独自の方法で優先順位がもっとも高いプロセスを選択して、グローバル実行待ち行列に入れます。

図 12–5 カーネルディスパッチ待ち行列

この図は、制限されない優先順位の逆転を示しています。

各クラスには、そのクラスのプロセスに適用される優先順位レベルのセットがあります。クラス固有のマッピングによって、この優先順位がグローバル優先順位のセットに割り当てられます。グローバルスケジューリング優先順位マッピングセットは、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 の使用法

動作中のクラスのスケジューリング制御は、priocntl(2) で処理します。fork(2)exec(2) を実行した場合、クラスの属性は、優先順位の制御に必要なスケジューリングパラメータやアクセス権とともに継承されます。この継承は、RT クラスと TS クラスの両方に当てはまります。

priocntl(2) は、システムコールが適用されるリアルタイムプロセス、プロセスのセット、またはクラスを指定するインタフェースです。priocntlset(2) も、システムコールを適用するプロセスのセット全体を指定する、より一般的なインタフェースを提供します。

priocntl(2) のコマンド引数は、PC_GETCIDPC_GETCLINFOPC_GETPARMSPC_SETPARMS のいずれかにします。呼び出し側プロセスの実 ID または実効 ID は、対象となるプロセスの実 ID または実効 ID と一致するか、あるいは、スーパーユーザー特権を持つ必要があります。

PC_GETCID

このコマンドは、認識可能なクラス名を含む構造体の名前フィールドを受け入れます。クラス ID とクラス属性データの配列が返されます。

PC_GETCLINFO

このコマンドは、認識可能なクラス識別子を含む構造体の ID フィールドを受け入れます。クラス名とクラス属性データの配列が返されます。

PC_GETPARMS

このコマンドは、指定されたプロセスのうち、1 つのプロセスのスケジューリングクラス識別子またはクラス固有のスケジューリングパラメータを返します。idtypeid によって大きなセットが指定された場合でも、PC_GETPARMS は 1 つのプロセスのパラメータだけを返します。どのプロセスを選択するかはクラスによって決まります。

PC_SETPARMS

このコマンドは、指定されたプロセス (複数可) のスケジューリングクラスまたはクラス固有のスケジューリングパラメータを設定します。

その他のインタフェース呼び出し

sched_get_priority_max

指定された方針の最大値を返します。

sched_get_priority_min

指定された方針の最小値を返します。詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください。

sched_rr_get_interval

指定された timespec 構造体を現在の実行時間制限に更新します。詳細は、sched_get_priority_max(3RT) のマニュアルページを参照してください。

sched_setparamsched_getparam

指定されたプロセスのスケジューリングパラメータを設定または取得します。

sched_yield

呼び出し側プロセスがプロセスリストの先頭に戻るまで、呼び出し側プロセスをブロックします。

スケジューリングを制御するユーティリティー

プロセスのスケジューリングを制御するシステム管理用ユーティリティーには、dispadmin(1M) および priocntl(1) があります。どちらのユーティリティーも priocntl(2) システムコールをサポートし、オプションに互換性があり、モジュールもロード可能です。これらのユーティリティーは、実行時にリアルタイムプロセスのスケジューリングを制御するシステム管理機能を提供します。

priocntl(1)

priocntl(1) コマンドは、プロセスのスケジューリングパラメータの設定および取得を行います。

dispadmin(1M)

dispadmin(1M) ユーティリティーに -l コマンド行オプションを付けると、実行時に現在のプロセスのすべてのスケジューリングクラスが表示されます。リアルタイムクラスを表す引数として RT-c オプションの後ろに指定すると、プロセスのスケジューリングを変更することもできます。

dispadmin(1M) のクラスオプションは次のとおりです。

-l

現在設定されているスケージュリングクラスを表示する

-c

パラメータを表示または変更するクラスを指定する

-g

指定されたクラスのディスパッチパラメータを取得する

-r

-g オプションとともに使用し、タイムカンタムの精度を指定する

-s

値が保存されているファイルを指定する

ディスパッチパラメータが保存されているクラス固有のファイルは実行時にもロードできます。このファイルを使用して、ブート時に確立されたデフォルトの値を新しい優先順位のセットで置き換えることができます。このクラス固有のファイルは、-g オプションで使用される書式で引数を表明する必要があります。RT クラスのパラメータについては rt_dptbl(4) を参照し、例については、例 12–1 を参照してください。

システムに RT クラスのファイルを追加するには、次のモジュールが存在する必要があります。

次の手順で、RT クラスのディスパッチテーブルのインストールを行います。

  1. 次のコマンドでクラス固有のモジュールをロードする。module_name にはクラス固有のモジュールを指定する。


    # modload /kernel/sched/module_name
    
  2. 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 ヘッダファイルでも説明されています)。

rt_globpri

この優先順位レベルに関係付けられているグローバルスケジューリング優先順位。dispadmin(1M) では rt_globpri の値は変更できない

rt_quantum

このレベルのプロセスに割り当てられるタイムカンタムの長さを目盛で表したもの。詳細は、「タイムスタンプインタフェース」を参照してください。タイムカンタム値は、特定のレベルのプロセスのデフォルト値、つまり開始値。リアルタイムプロセスのタイムカウンタを変更するには、priocntl(1) コマンドまたは priocntl(2) システムコールを使用する

config_rt_dptbl の再構成

リアルタイムのシステム管理者は、いつでも 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[] 値の例を示します。


例 12–1 RT クラスのディスパッチパラメータ

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,										}

メモリーのロック

メモリーのロックは、リアルタイムアプリケーションにとって最重要事項の 1 つです。リアルタイム環境では、応答時間を減らし、ページングとスワッピングを防ぐために、プロセスは連続してメモリー内に常駐することが保証されなければなりません。

この節では、SunOS において、リアルタイムアプリケーションが利用できるメモリーロックのメカニズムについて説明します。

SunOS では、プロセスがメモリーに常駐するかどうかは、プロセスの現在の状態、使用できる全物理メモリー、動作中のプロセス数、およびプロセッサのメモリーに対する要求によって決まります。このような方法は、タイムシェアリング環境には適合します。しかし、リアルタイム環境には受け入れられないことがよくあります。リアルタイム環境では、プロセスのメモリーアクセスとディスパッチ応答時間を減らすために、プロセスはメモリー常駐を保証する必要があります。

SunOS のリアルタイムメモリーロックは、ライブラリルーチンのセットで提供されます。このようなライブラリルーチンを使用すると、スーパーユーザー特権で実行しているプロセスは、自分の仮想アドレス空間の指定された部分を物理メモリーにロックできます。このようにしてロックされたページは、ロックが解除されるか、プロセスが終了するまでページングの対象になりません。

オペレーティングシステムには、一度にロックできるページ数にシステム全体の制限があります。この制限は調整可能なパラメータであり、デフォルト値はブート時に計算されます。デフォルト値は、ページフレーム数から一定の割合 (現在は 10 %に設定) を引いた値が基本になります。

ページのロック

mlock(3C) への呼び出しは、メモリーの 1 セグメントをシステムの物理メモリーにロックするように要求します。たとえば、指定されたセグメントを構成するページにページフォルトが発生したと仮定します。すると、各ページのロックカウントが 1 だけ増えます。ロックのカウントが 0 より大きいページは、ページング操作から除外されます。

特定のページを異なるマッピングで複数のプロセスを使って何度もロックできます。2 つの異なったプロセスが同じページをロックすると、両方のプロセスがロックを解除するまで、そのページはロックされたままです。ただし、マッピング内でページロックは入れ子にしません。同じプロセスが同じアドレスに対してロックインタフェースを何度も呼び出した場合でも、ロックは一度のロック解除要求で削除されます。

ロックが実行されているマッピングが削除されると、そのメモリーセグメントのロックは解除されます。ファイルを閉じるまたは切り捨てることによってページが削除された場合も、ロックは暗黙的に解除されます。

fork(2) 呼び出しが行われたあと、ロックは子プロセスには継承されません。したがって、メモリーをロックしているプロセスが子プロセスをフォークした場合、子プロセスは自分でメモリーロック操作を行い、自分のページをロックする必要があります。そうしないと、プロセスをフォークした場合に通常必要となるページのコピー即時書き込みを行わなければならなくなります。

ページのロック解除

メモリーによるページのロックを解除するために、プロセスは munlock(3C) 呼び出しによって、ロックされている仮想記憶のセグメントを解放するように要求します。munlock により、指定された仮想ページのロックカウントは 1 だけ減ります。ページのロックカウントが 0 まで減ると、そのページは普通にスワップされます。

全ページのロック

スーパーユーザープロセスは、mlockall(3C) 呼び出しによって、自身のアドレス空間内の全マッピングをロックするように要求できます。MCL_CURRENT フラグが設定されている場合は、既存のメモリマッピングがすべてロックされます。MCL_FUTURE フラグが設定されている場合は、既存のマッピングに追加されるマッピングまたは既存のマッピングを置き換えるマッピングはすべて、メモリー内にロックされます。

スティッキロックの復元

ページのロックカウントが 65535 (0xFFFF) に達すると、そのページは永久にロックされます。値 0xFFFF は実装によって定義されています。この値は将来のリリースで変更される可能性があります。このようにしてロックされたページは、ロックを解除できません。復元するにはシステムをリブートしてください。

高性能入出力

この節では、リアルタイムプロセスでの入出力について説明します。SunOS では、高速で非同期的な入出力操作を実行するために、インタフェースと呼び出しの 2 つのセットをライブラリで提供しています。POSIX 非同期入出力インタフェースは最新の標準です。SunOS 環境は、情報の消失やデータの不一致を防止するために、ファイルおよびメモリー内の同期操作とモードを提供します。

標準の UNIX 入出力は、アプリケーションのプログラマと同期します。read(2) または write(2) を呼び出すアプリケーションは、通常はシステムコールが終了するまで待ちます。

リアルタイムアプリケーションは、入出力に非同期でバインドされた特性を必要とします。非同期入出力呼び出しを発行したプロセスは、入出力操作の完了を待たずに先に進むことができます。呼び出し側は、入出力操作が終了すると通知されます。

非同期入出力は、任意の SunOS ファイルで使用できます。ファイルは同期的に開かれますが、特別なフラグ設定は必要ありません。非同期入出力転送には、 呼び出し、要求、操作の 3 つの要素があります。アプリケーションは非同期入出力インタフェースを呼び出し、入出力要求は待ち行列に置かれ、呼び出しはすぐに戻ります。ある時点で、システムは要求を待ち行列から取り出し、入出力操作を開始します。

非同期入出力要求と標準入出力要求は、任意のファイル記述子で混在させることができます。システムは、読み取り要求と書き込み要求の特定の順序を維持しません。システムは、保留状態にあるすべての読み取り要求と書き込み要求の順序を任意に並べ替えます。特定の順序を必要とするアプリケーションは、前の操作の完了を確認してから従属する要求を発行しなければなりません。

POSIX 非同期入出力

POSIX 非同期入出力は、aiocb 構造体を使用して行います。aiocb 制御ブロックは、各非同期入出力要求を識別し、すべての制御情報を持っています。制御ブロックを使用できる要求は一度に 1 つだけです。その要求が完了すると、制御ブロックはまた使用できるようになります。

一般的な POSIX 非同期入出力操作は、aio_read(3RT) または aio_write(3RT) 呼び出しによって開始します。ポーリングまたはシグナルを使用すると、操作の完了を判断できます。操作の完了にシグナルを使用する場合は、各操作に一意のタグを付けることができます。このタグは生成されたシグナルの si_value 構成要素に戻されます。siginfo(3HEAD) のマニュアルページを参照してください。

aio_read

aio_read(3RT) は、読み取り操作の開始のために非同期入出力制御ブロックを使用して呼び出します。

aio_write

aio_write(3RT) は、書き込み操作の開始のために非同期入出力制御ブロックを指定して呼び出します。

aio_return, aio_error

aio_return(3RT) および aio_error(3RT) はそれぞれ、操作が完了していると判明したあとに、戻り値とエラー値を取得するために呼び出します。

aio_cancel

aio_cancel(3RT) は、保留状態の操作を取り消すために非同期入出力制御ブロックを指定して呼び出します。aio_cancel は、制御ブロックによって要求が指定されている場合、指定された要求を取り消します。aio_cancel はまた、制御ブロックによって指定されたファイル記述子に対して保留されているすべての要求を取り消します。

aio_fsync

aio_fsync(3RT) は、指定されたファイル上で保留されているすべての入出力操作に対する非同期的な fsync(3C) または fdatasync(3RT) 要求を待ち行列に入れます。

aio_suspend

aio_suspend(3RT) は、1 つ以上の先行する非同期入出力要求が同期的に行われるかのように呼び出し側を一時停止します。

Solaris 非同期入出力

この節では、Solaris オペレーティング環境における非同期入出力について説明します。

通知 (SIGIO)

非同期入出力呼び出しが正常に返ったとき、入出力操作は単に待ち行列に入って、実行が行われるのを待ちます。実際の操作は、戻り値と潜在的なエラー識別子を持っています。呼び出しが同期的に行われた場合、この戻り値と潜在的なエラー識別子は呼び出し側に戻されます。入出力が終了すると、戻り値とエラー値は両方とも、ユーザーが要求時に aio_result_t へのポインタとして指定した位置に格納されます。aio_result_t 構造体は、<sys/asynch.h> に次のように定義されています。

typedef struct aio_result_t {
 	ssize_t	aio_return; /* return value of read or write */
 	int 		aio_errno;  /* errno generated by the IO */
 } aio_result_t;

aio_result_t が変更されると、入出力要求を行なったプロセスに SIGIO シグナルが配信されます。

2 つ以上の非同期入出力操作を保留しているプロセスは、SIGIO シグナルの原因を特定できません。SIGIO を受け取ったプロセスは、SIGIO を生じた原因となる条件をすべて確認する必要があります。

aioread の使用法

aioread(3AIO) ルーチンは read(2) の非同期バージョンです。通常の読み取り引数に加えて、aioread(3AIO) はファイルの位置を指定する引数と aio_result_t 構造体のアドレスを指定する引数を取ります。aio_result_t 構造体には、操作の結果情報が格納されます。ファイル位置には、操作の前にファイル内で行うシークを指定します。aioread(3AIO) 呼び出しが成功したか失敗したかに関係なく、ファイルポインタは更新されます。

aiowrite の使用法

aiowrite(3AIO) ルーチンは write(2) の非同期バージョンです。通常の書き込み引数に加えて、aiowrite(3AIO) はファイルの位置を指定する引数と aio_result_t 構造体のアドレスを指定する引数を取ります。aio_result_t 構造体には、操作の結果情報が格納されます。

ファイル位置は、この操作が行われる前に、ファイル内でシーク操作が実行されることを指定します。aiowrite(3AIO) 呼び出しが成功すると、ファイルポインタはシークと書き込みが成功した場合の位置に変更されます。ファイルポインタは書き込みを行なったあと、以降の書き込みができなくなった場合も変更されます。

aiocancel の使用法

aiocancel(3AIO) ルーチンは、aio_result_t 構造体を引数として指定した非同期要求を取り消そうとします。aiocancel(3AIO) 呼び出しは、要求がまだ待ち行列にある場合にのみ成功します。操作がすでに進行している場合、aiocancel(3AIO) は失敗します。

aiowait の使用法

aiowait(3AIO) を呼び出すと、少なくとも 1 つの未処理の非同期入出力操作が完了するまで、呼び出し側プロセスはブロックされます。タイムアウトパラメータは、入出力の完了を待つ最大インターバルを指します。0 のタイムアウト値は、待つ必要がないことを指定します。aiowait(3AIO) は、完了した操作の aio_result_t 構造体へのポインタを返します。

poll() の使用法

非同期入出力イベントの完了を同期的に決定するには、SIGIO 割り込みに依存するのではなく、poll(2) を使用します。ポーリングを使用すると、SIGIO 割り込みの原因を調べることもできます。

あまり多くのファイルで poll(2) を使用すると、処理が遅くなります。この問題は、poll(7d) で解決します。

poll ドライバの使用法

/dev/poll を使用すると、多数のファイル記述子のポーリングを高いスケーラビリティーで行うことができます。このスケーラビリティーは、新しい API のセットと新しいドライバ /dev/poll によって実現されます。/dev/poll API は poll(2) を置き換えるものではなく、どちらかを選択して使用するものです。poll(7d) を使用すると、/dev/poll API の詳細と例を提供できます。適切に使用すると、/dev/poll API は poll(2) よりも高いスケーラビリティーを提供します。この API は、特に、次の条件を満たすアプリケーションに適します。

close の使用法

ファイルを閉じるには、close(2) を呼び出します。close(2) を呼び出すと、未処理の非同期入出力要求のうち、閉じることができるものを取り消します。close(2) は、取り消せない操作の完了を待ちます。詳細は、aiocancel の使用法」を参照してください。close(2) 呼び出しが戻ると、そのファイル記述子について保留状態にある非同期入出力要求はなくなります。ファイルが閉じられると、取り消されるのは指定したファイル記述子に対する待ち行列内にある非同期入出力要求だけです。ほかのファイル記述子について、保留状態にある入出力要求は取り消されません。

同期入出力

アプリケーションは、情報が安定した記憶領域に書き込まれたことや、ファイル変更が特定の順序で行われることを保証する必要がある場合があります。同期入出力は、このような場合のために用意されています。

同期モード

SunOS では、データが書き込み操作用としてファイルに正常に転送されるには、システムがファイルを開いたときには、以前に書き込まれたデータを読み取ることができることを保証している必要があります。この確認は、物理的な記憶媒体に障害がないことを想定しています。また、データが読み取り操作用として正常に転送されるには、要求側プロセスが物理記憶媒体上にあるデータのイメージを利用できる必要があります。入出力操作は、関連付けられているデータが正しく転送されたか、操作が失敗と診断された場合に完了します。

入出力操作は、次の場合に同期入出力データの整合性を保証します。

ファイルの同期

fsync(3C) および fdatasync(3RT) は明示的にファイルと二次記憶領域の同期をとります。

fsync(3C) ルーチンは、入出力ファイルの整合性の保証レベルでインタフェースの同期をとることを保証します。fdatasync(3RT) は、入出力データの整合性の保証レベルでインタフェースの同期をとることを保証します。

アプリケーションは、操作が完了する前に、各入出力操作の同期をとるように指定できます。open(2) または fcntl(2) を使用して、ファイル記述子に O_DSYNC フラグを設定すると、操作が完了したと見なされる前に、すべての入出力書き込みは入出力データ完了に達します。ファイル記述子に O_SYNC フラグを設定すると、操作が完了したと見なされる前に、すべての入出力書き込みは入出力ファイル完了に達します。ファイル記述子に O_RSYNC フラグを設定すると、すべての入出力読み取り (read(2)aio_read(3RT)) はファイル記述子に設定したのと同じ完了レベルに達します。ファイル記述子に設定するのは、O_DSYNC または O_SYNC のどちらでもかまいません。

プロセス間通信

この節では、リアルタイム処理と関連する、SunOS インタフェースについて説明します。また、シグナル、パイプ、FIFO、メッセージ待ち行列、共有メモリー、ファイルマッピング、およびセマフォーについても説明します。プロセス間通信に役立つライブラリ、インタフェース、およびルーチンについては、第 7 章プロセス間通信を参照してください。

シグナルの処理

次のように、送信側は sigqueue(3RT) を使用して、少量の情報とともにシグナルをターゲットプロセスに送信します。

以降に発生する保留状態のシグナルも待ち行列に入るので、ターゲットプロセスは指定されたシグナルの SA_SIGINFO ビットを設定する必要があります。詳細は、sigaction(2) のマニュアルページを参照してください。

ターゲットプロセスは通常、シグナルを非同期的に受信します。シグナルを同期的に受信するには、シグナルをブロックして、sigwaitinfo(3RT) または sigtimedwait(3RT) を呼び出します。詳細は、sigprocmask(2) のマニュアルページを参照してください。この手順によって、シグナルは同期的に受信されるようになります。このとき、sigqueue(3RT) の呼び出し側が送信した値は siginfo_t 引数の si_value メンバーに格納されます。シグナルのブロックを解除しておくと、シグナルは siginfo_t 引数の si_value に格納された値とともに、sigaction(2) によって指定されたシグナルハンドラに配信されます。

あるプロセスが送信できるシグナルの数は関連する値で指定されており、残りは送信されないままになります。sigqueue(3RT) を最初に呼び出したとき、{SIGQUEUE_MAX} 個のシグナルが入る記憶情報域が割り当てられます。次に、sigqueue(3RT) を呼び出すと、ターゲットプロセスの待ち行列にシグナルが正常に入るか、制限時間内で失敗します。

パイプ、名前付きパイプ、およびメッセージ待ち行列

パイプ、名前付きパイプ、およびメッセージ待ち行列は、文字入出力デバイスと同様に動作します。ただし、これらのインタフェースは接続には異なる方法を使用します。パイプについての詳細は、「プロセス間のパイプ」を参照してください。名前付きパイプについての詳細は、「名前付きパイプ」を参照してください。メッセージ待ち行列についての詳細は、「System V メッセージ」および 「POSIX メッセージ」を参照してください。

セマフォーの使用法

セマフォーも System V と POSIX の両方のスタイルで提供されます。詳細は、「System V セマフォー」および 「POSIX セマフォー」を参照してください。

この章で前述した手法によって優先順位の逆転を明示的に回避しない限り、セマフォーを使用すると、優先順位の逆転が発生する場合があるので注意してください。

共有メモリー

プロセスがもっとも高速に通信する方法は、メモリーの共有セグメントを直接使用する方法です。3 つ以上のプロセスが同時に共有メモリーに読み書きしようとすると、メモリーの内容が正確でなくなる可能性があります。これは、共有メモリーを使用する場合のもっとも大きな問題です。

非同期ネットワーク通信

この節では、ソケットまたはリアルタイムアプリケーション用のトランスポートレベルインタフェース (TLI) を使用した非同期ネットワーク通信について説明します。ソケットを使用した非同期ネットワーキングを行うには、SOCK_STREAM タイプのオープンソケットを非同期および非ブロックに設定します。非同期ソケットについての詳細は、「ソケットの拡張機能」を参照してください。TLI イベントの非同期ネットワーク処理は、STREAMS 非同期機能と TLI ライブラリルーチンの非ブロックモードの組み合わせによってサポートされます。

トランスポートレベルインタフェース (TLI) についての詳細は、第 9 章XTI と TLI を使用したプログラミングを参照してください。

ネットワーキングのモード

ソケットとトランスポートレベルインタフェース (TLI) はどちらも 2 つのサービスモード、「コネクションモード」と「コネクションレスモード」を提供します。

コネクションモード」サービスは回線指向です。このサービスを使用すると、データは、確立されたコネクション経由で、信頼できる順序付け方法で伝送されます。このサービスはまた、データ転送フェーズ中のアドレス解決および転送のオーバーヘッドを回避する識別処理を提供します。このサービスは、比較的長時間持続するデータストリーム指向の対話を必要とするアプリケーションに適しています。

コネクションレスモード」サービスはメッセージ指向であり、複数のユニット間の論理的な関係が要求されない、独立したユニットでのデータ伝送をサポートします。単一のサービス要求は、データのユニットを配信するために必要なすべての情報を送信側からトランスポートプロバイダに渡します。このサービス要求には、着信先アドレスや配信されるデータが含まれます。コネクションレスモードサービスは、対話が短時間で済み、保証された順番どおりの方法でデータを配信する必要がないアプリケーションに適しています。一般的に、コネクションレスモードによる伝送は信頼性が低いと言えます。

タイミング機能

この節では、SunOS におけるリアルタイムアプリケーションで利用できるタイミング機能について説明します。このようなメカニズムをリアルタイムアプリケーションで使用するには、この節で説明する各ルーチンのマニュアルページの詳細な情報が必要です。

SunOS のタイミングインタフェースは、「タイムスタンプ」と「インターバルタイマー」の 2 つの機能に分類できます。「タイムスタンプインタフェース」は経過時間を測定する方法を提供します。したがって、タイムスタンプインタフェースを使用すると、アプリケーションはある状態の持続時間やイベント間の時間を測定できます。「インターバルタイマー」を使用すると、アプリケーションはさまざまな活動を指定された時刻に呼び起こし、時間の経過に基づいてスケジューリングできます。

タイムスタンプインタフェース

タイムスタンプは 2 つのインタフェースによって提供されます。gettimeofday(3C) は、グリニッジ標準時間 1970 年 1 月 1 日午前 0 時からの秒数とマイクロ秒数によって時間を表し、現在の時間を timeval 構造体に提供します。clock_gettime は、CLOCK_REALTIMEclockid を使用して、gettimeofday(3C) が戻すタイムインターバルと同じ時間を秒とナノ秒で表し、現在の時間を timespec 構造体に提供します。

SunOS はハードウェア定期タイマーを使用します。このハードウェア定期タイマーが唯一の時間情報源であるワークステーションもあります。ハードウェア定期タイマーが唯一の時間情報源である場合、タイムスタンプの精度はハードウェア定期タイマーの精度に制限されます。その他のプラットフォームでは、タイマーレジスタの精度が 1 マイクロ秒である場合、そのタイムスタンプの精度は 1 マイクロ秒であることを意味します。

インターバルタイマーインタフェース

リアルタイムアプリケーションは、インターバルタイマーを使用して動作をスケジューリングすることがよくあります。インターバルタイマーには「単発」型と「周期」型の 2 つの種類があります。

単発タイマーは、現在時間または絶対時間に相対的な有効期限に設定されるタイマーで、有効期限が終了すると解除されます。この単発タイマーは、データを記憶領域に転送したあとのバッファーの消去や操作のタイムアウトの管理に便利です。

周期タイマーには、初期有効期限 (絶対時間または相対時間) と繰り返しインターバルが設定されています。インターバルタイマーの有効期限が経過するたびに、インターバルタイマーは繰り返して再ロードされます。そして、インターバルタイマーは自動的に再設定されます。このタイマーはデータの記録やサーボの制御に便利です。インターバルタイマー機能を呼び出すとき、システムのハードウェア定期タイマーの精度より小さな時間値は、ハードウェア定期タイマーのインターバルの次の倍数に丸められます。このインターバルは通常 10 ミリ秒です。

SunOS には、2 種類のタイマーインタフェースがあります。setitimer(2) および getitimer(2) インタフェースは「BSD タイマー」という固定設定タイマーを動作させ、timeval 構造体を使用して、時間インターバルを指定します。timer_create(3RT) で作成される POSIX タイマーは POSIX クロック CLOCK_REALTIME を動作させます。POSIX タイマーの動作は timespec 構造体によって表されます。

getitimer(2) および setitimer(2) 関数はそれぞれ、指定された BSD インターバルタイマーの値を取得および確立します。プロセスは 3 つの BSD インターバルタイマーを利用できます (ITIMER_REAL で指定されるリアルタイムタイマーを含む)。BSD タイマーが設定されており、有効になっている (期限切れになることが許可されている) 場合、システムはタイマーを設定したプロセスに適切なシグナルを送信します。

timer_create(3RT) ルーチンは TIMER_MAX 個までの POSIX タイマーを作成できます。呼び出し側はタイマーの有効期限が経過したときに、どのシグナルとそれに関連する値をプロセスに送信するかを指定できます。timer_settime(3RT) および timer_gettime(3RT) ルーチンはそれぞれ、指定された POSIX インターバルタイマーの値を取得および確立します。必要なシグナルの配信が保留状態の間でも、POSIX タイマーは期限切れになることがあります。タイマーの有効期限がカウントされるので、timer_getoverrun(3RT) でそのカウントを取得します。timer_delete(3RT) で POSIX タイマーの割り当てを解除します。

次の例に、setitimer(2) を使用して、定期割り込みを生成する方法と、タイマー割り込みの到着を制御する方法を示します。


例 12–2 タイマー割り込みの制御

#include	<unistd.h>
#include	<signal.h>
#include	<sys/time.h>

#define TIMERCNT 8

void timerhandler();
int	 timercnt;
struct	 timeval alarmtimes[TIMERCNT];

main()
{
	struct itimerval times;
	sigset_t	sigset;
	int		i, ret;
	struct sigaction act;
	siginfo_t	si;

	/* block SIGALRM */
	sigemptyset (&sigset);
	sigaddset (&sigset, SIGALRM);
	sigprocmask (SIG_BLOCK, &sigset, NULL);

	/* set up handler for SIGALRM */
	act.sa_action = timerhandler;
	sigemptyset (&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	sigaction (SIGALRM, &act, NULL);
	/*
	 * set up interval timer, starting in three seconds,
	 *	then every 1/3 second
	 */
	times.it_value.tv_sec = 3;
	times.it_value.tv_usec = 0;
	times.it_interval.tv_sec = 0;
	times.it_interval.tv_usec = 333333;
	ret = setitimer (ITIMER_REAL, &times, NULL);
	printf ("main:setitimer ret = %d\n", ret);

	/* now wait for the alarms */
	sigemptyset (&sigset);
	timerhandler (0, si, NULL);
	while (timercnt < TIMERCNT) {
		ret = sigsuspend (&sigset);
	}
	printtimes();
}

void timerhandler (sig, siginfo, context)
	int		sig;
	siginfo_t	*siginfo;
	void		*context;
{
	printf ("timerhandler:start\n");
	gettimeofday (&alarmtimes[timercnt], NULL);
	timercnt++;
	printf ("timerhandler:timercnt = %d\n", timercnt);
}

printtimes ()
{
	int	i;

	for (i = 0; i < TIMERCNT; i++) {
		printf("%ld.%0l6d\n", alarmtimes[i].tv_sec,
				alarmtimes[i].tv_usec);
	}
}