サブシステム管理機能は、μT-Kernel上で動作するミドルウェア等を実装するために、「サブシステム」と呼ばれるユーザ定義の機能をカーネルに追加し、μT-Kernel本体の機能を拡張するための機能である。μT-Kernel/SMの一部の機能についても、サブシステム管理機能を利用して実装されている。
サブシステムは、ユーザ定義のシステムコール(「拡張SVC」と呼ぶ)を実行するための拡張SVCハンドラのほか、例外発生時の処理を行うブレーク関数、デバイス等からのイベント発生時の処理を行うイベント処理関数、タスクのリソースグループ毎に起動時や終了時の処理を行うためのスタートアップ関数とクリーンアップ関数、およびリソース管理ブロックから構成される(図15)。
このうち、拡張SVCハンドラはアプリケーションなどからの要求を直接受け付けて処理する。一方、ブレーク関数、イベント処理関数、スタートアップ関数、クリーンアップ関数は、いわゆるコールバック型の関数であり、カーネルからの要求を受け付けて処理する。
補足事項 | |
---|---|
プロセス管理機能、ファイル管理機能などを含むT-Kernel Extension(T-Kernel Standard Extension)の機能も、サブシステム管理機能を利用して実装される。このほか、サブシステム管理機能を利用して実装されたT-Kernel用のミドルウェアの例として、TCP/IPマネージャ、USBマネージャ、PCカードマネージャなどがある。 サブシステム管理機能は、ITRONの拡張SVCハンドラや拡張サービスコールに相当する機能であるが、単なるユーザ定義システムコールの追加だけではなく、追加したシステムコールの処理に付随して必要となるリソース管理の機能や例外発生時の処理機能も提供することによって、高度で複雑な機能を持つミドルウェアを実現可能としたものである。 サブシステム管理機能では、タスクの所属するリソースグループを単位として、リソースを管理する。一方、T-Kernelの上位ミドルウェアであるT-Kernel Extension(T-Kernel Standard Extension)では、T-Kernelのリソースグループの機能を使ってプロセスの機能を実現する。このような関係により、サブシステムのリソース管理をプロセス毎に行うことができ、プロセスの生成時(起動時)や終了時には、その時点で定義されている各サブシステムに含まれるスタートアップ関数とクリーンアップ関数が自動的に実行される。たとえば、プロセスの終了時にクローズされていないファイルを自動的にクローズしたい場合には、ファイル管理のサブシステムに含まれるクリーンアップ関数の中で、このような処理を実行すればよい。 なお、T-Kernel本体の機能を拡張するための機能としては、サブシステム管理機能のほかに、デバイスドライバの機能がある。サブシステムもデバイスドライバも、T-Kernel本体とは独立した機能モジュールであり、対応するバイナリプログラムを共有空間にロードすることによって、T-Kernel上のタスクからこれらの機能を呼び出して利用できるようになる。また、両者とも保護レベル0で動作する。一方、両者の相違点としては、デバイスドライバを呼び出す際のAPIがオープン/クローズ、リード/ライト型に固定されているのに対して、サブシステムを呼び出す際のAPIは自由に定義できる。また、サブシステムの場合はリソースグループ(プロセス)の生成時(起動時)や終了時に自動的にリソースの管理を行う機能があるが、デバイスドライバの場合はこのような機能がない。 サブシステムは、サブシステムID( |
pk_dssy
の内容
ATR |
ssyatr
| Subsystem Attributes | サブシステム属性 |
PRI |
ssypri
| Subsystem Priority | サブシステム優先度 |
FP |
svchdr
| Extended SVC Handler Address | 拡張SVCハンドラアドレス |
FP |
breakfn
| Break Function Address | ブレーク関数アドレス |
FP |
startupfn
| Startup Function Address | スタートアップ関数アドレス |
FP |
cleanupfn
| Cleanup Function Address | クリーンアップ関数アドレス |
FP |
eventfn
| Event Handling Function Address | イベント処理関数アドレス |
SZ |
resblksz
| Resource Control Block Size | リソース管理ブロックサイズ(バイト数) |
──(以下に実装独自に他の情報を追加してもよい)── |
以下のいずれかのサービスプロファイルが有効に設定されているとき、サブシステム優先度(ssypri
)の指定が可能である。
また、以下のサービスプロファイルが有効に設定されている場合に限り、ブレーク関数の指定が可能である。
ssid
のサブシステムを定義する。
1つのサブシステムに1つのサブシステムIDを、他のサブシステムと重複しないように割り当てなければならない。カーネルに自動割当ての機能はない。
サブシステムIDは1~9がT-Kernel用に予約されている。10~255がミドルウェア等で使用できる番号となる。ただし、使用可能なサブシステムIDの最大値は実装定義であり、255より小さい場合がある。
ssyatr
は、下位側がシステム属性を表し、上位側が実装独自属性を表す。ssyatr
のシステム属性には、現在のバージョンでは割当てがなく、システム属性は使われていない。
ssypri
は、サブシステムの優先度で、スタートアップ関数、クリーンアップ関数、イベント処理関数が優先度順に呼び出される。同一優先度の場合の呼出順は不定である。サブシステム優先度は、1が最も優先度が高く、数値が大きくなるにしたがって優先度が下がる。指定できる優先度の範囲は実装定義だが、少なくとも1~16が指定できなければならない。
breakfn
, startupfn
, cleanupfn
, eventfn
はそれぞれ NULL を指定することができる。NULL を指定した場合、対応する関数は呼び出されない。
pk_dssy
=NULL とした場合は、サブシステムの定義を抹消する。このとき、ssid
のサブシステムのリソース管理ブロックもすべて削除される。
リソース管理ブロック
リソース(資源)をグループ分けし、その所属などを管理するためのメモリブロックである。resblksz
で指定したサイズのメモリが、リソースグループごとに1つずつ用意される。resblksz
=0を指定すれば、リソース管理ブロックは割り当てられない。しかし、その場合もリソースID(tk_cre_res 参照)は割り当てられる。
タスクは、いずれかのリソースグループに所属する。あるタスクからサブシステムに対して要求があり、サブシステム内のリソースがそのタスクに割り当てられた場合に、その割当て情報をリソース管理ブロックに登録する。どのようなリソースをリソース管理ブロックに登録するか、またどのようにして登録するかは、サブシステム側で決める。
リソース管理ブロックの内容についてはカーネルは関知しないので、サブシステム側で自由に利用できる。ただし、resblksz
はできる限り小さなサイズとする。より大きなメモリブロックが必要であれば、メモリブロックは別途サブシステム側で用意し、そのアドレスをリソース管理ブロックに登録するようにする。
リソース管理ブロックは、システム空間の常駐メモリである。
拡張SVCハンドラ
アプリケーションなどからの要求の受け付け口となる。サブシステムのAPIとなる部分である。システムコールと同様の方法で呼び出すことが可能で、一般的にはトラップ命令などで呼び出す。
拡張SVCハンドラは次の形式となる。
INT svchdr( void *pk_para, FN fncd ) { /* fncd により分岐して処理 */ return retcode; /* 拡張SVCハンドラの終了 */ }
fncd
は機能コードである。機能コードの下位8ビットにはサブシステムIDが入る。残りの上位ビットは、サブシステム側で任意に使用できる。通常は、サブシステム内の機能コードとして使用する。ただし、機能コードは正の値でなければならないため、最上位ビットは必ず0となる。
pk_para
は呼出側から渡されたパラメータをパケット形式にしたものである。パケットの形式は、サブシステム側で任意に決めることができる。一般的には、C言語で関数に引数を渡すときのスタックの形式と同じになる。これは、多くの場合C言語の構造体の形式と同じである。
拡張SVCハンドラからの戻値は、そのまま呼出元へ関数の戻値として戻される。原則として、負の値をエラー値、0または正の値を正常時の戻値とする。なお、何らかの理由で拡張SVCの呼出に失敗した場合は、拡張SVCハンドラは呼び出されずに、呼出元にはカーネルの返すエラーコード(負の値)が返されるため、それと混同しないようにしておくことが望ましい。
拡張SVCの呼出側の形式はカーネルの実装に依存する。ただし、サブシステムのAPIとしてはC言語の関数の形式で、カーネルの実装に依存しないように規定しなければならない。サブシステムは、C言語の関数の形式からカーネル依存の拡張SVCの呼出形式に変換するための、インタフェースライブラリを用意しなければならない。
拡張SVCハンドラは、準タスク部として実行される。
タスク独立部からも呼出が可能で、タスク独立部から呼び出された場合は、拡張SVCハンドラもタスク独立部として実行される。
ブレーク関数
ブレーク関数は、拡張SVCハンドラの実行中のタスクに、タスク例外が発生した場合に呼び出される関数である。
ブレーク関数が呼び出されたら、タスク例外が発生した現在実行中の拡張SVCハンドラの処理を速やかに中止し、拡張SVCハンドラから呼出元に戻らなければならない。現在実行中の拡張SVCハンドラの処理を中止するための処理をブレーク関数で行う。
ブレーク関数は次の形式となる。
void breakfn( ID tskid ) { /* 拡張SVCハンドラの実行中止処理 */ }
tskid
はタスク例外が発生したタスクのIDである。
ブレーク関数は、tk_ras_tex によりタスク例外が発生されたときに呼び出される。また、拡張SVCハンドラがネストして呼び出されていた場合は、拡張SVCハンドラから戻ってネストが1段浅くなったときに、戻った先の拡張SVCハンドラに対応するブレーク関数が呼び出される。
ブレーク関数は、1回のタスク例外で、1つの拡張SVCハンドラに対して1回のみ実行される。
タスク例外が発生している状態で、さらにネストして拡張SVCを呼び出した場合、呼出先の拡張SVCハンドラに対してはブレーク関数は呼び出されない。
ブレーク関数は準タスク部として実行されるが、その要求タスクは次のようになる。tk_ras_tex の発行によるブレーク関数実行の場合は、tk_ras_tex を発行したタスクの準タスク部としてブレーク関数が実行される。一方、拡張SVCハンドラのネストが1段浅くなったときのブレーク関数実行の場合は、タスク例外の発生したタスク(拡張SVCハンドラを実行しているタスク)の準タスク部としてブレーク関数が実行される。したがって、ブレーク関数の実行タスクと拡張SVCハンドラの実行タスクが異なっている場合がある。このような場合、ブレーク関数と拡張SVCハンドラがタスクスケジューリングにしたがって同時に実行されることになる。
そのため、ブレーク関数の実行が終了する前に拡張SVCハンドラから呼出元に戻るケースが考えられるが、そのような場合には拡張SVCハンドラから呼出元に戻る直前の状態でブレーク関数が終了するまで待たされる。この待ち状態がタスク状態遷移のどの状態になるかは実装依存とするが、READY状態のまま(RUNNING状態になれないREADY状態)とすることが望ましい。また、ブレーク関数の終了を待つ間、タスクの優先順位が変化することがあるが、タスクの優先順位がどのようになるかは実装依存とする。
同様に、ブレーク関数の実行が終了するまで拡張SVCハンドラから拡張SVCを呼び出すことはできず、ブレーク関数の終了まで待たされる。
つまり、タスク例外が発生してからブレーク関数が終了するまでの間、タスク例外が発生したときの拡張SVCハンドラから抜けないようにしなくてはならない。
ブレーク関数と拡張SVCハンドラの要求タスクが異なるケース、すなわちブレーク関数と拡張SVCハンドラが異なるタスクとして実行されるケースにおいて、ブレーク関数のタスク優先度が拡張SVCハンドラのタスク優先度より低い場合は、ブレーク関数の実行期間だけ、拡張SVCハンドラのタスク優先度と同じ優先度までブレーク関数のタスク優先度が引き上げられる。逆に、ブレーク関数のタスク優先度が同じかより高い場合には、優先度は変更されない。変更される優先度は現在優先度で、ベース優先度は変更されない。
優先度変更はブレーク関数へ入る直前のみで、その後に拡張SVCハンドラ側の優先度が変更されても追従してブレーク関数側の優先度が変更されることはない。ブレーク関数実行中にブレーク関数側の優先度を変更した場合も、拡張SVCハンドラ側の優先度が変更されることはない。また、このときブレーク関数を実行中であることにより優先度の変更が制限されることはない。
ブレーク関数終了時には現在優先度をベース優先度に戻す。ただし、ミューテックスをロックしていた場合には、ミューテックスにより調整された優先度に戻す。(つまり、ブレーク関数の入口と出口で現在優先度を調整する機能があるのみで、それ以外については通常のタスク実行中と同じである。)
スタートアップ関数
tk_sta_ssy の呼出によって呼び出される。
リソース管理ブロックの初期化処理を行う。
スタートアップ関数は次の形式となる。
void startupfn( ID resid, INT info ) { /* リソース管理ブロックの初期化処理 */ }
resid
は初期化対象のリソースグループID、info
は任意のパラメータで、いずれも tk_sta_ssy で指定されたものである。
何らかの理由でリソース管理ブロックの初期化に失敗した場合も、スタートアップ関数は正常に終了させなければならない。リソース管理ブロックの初期化ができなかった場合、それによって正常に実行できないAPI(拡張SVC)が呼び出された時点で、そのAPIの戻値をエラーとする。
スタートアップ関数は tk_sta_ssy の発行タスクの準タスク部として実行される。
クリーンアップ関数
tk_cln_ssy の呼出によって呼び出される。
リソースの解放処理を行う。
クリーンアップ関数は次の形式となる。
void cleanupfn( ID resid, INT info ) { /* リソース解放処理 */ }
resid
はリソース解放の対象となるリソースグループID、info
は任意のパラメータで、いずれも tk_cln_ssy で指定されたものである。
何らかの理由でリソースの解放に失敗した場合でも、クリーンアップ関数は正常に終了させなければならない。エラーログを残すなどのエラー処理は、サブシステムで独自に決める。
クリーンアップ関数が終了した後、自動的にリソース管理ブロックは0クリアされる。また、クリーンアップ関数が定義されなかった場合(cleanupfn
=NULL)にも、tk_cln_ssy によりリソース管理ブロックは0クリアされる。
クリーンアップ関数は、tk_cln_ssy の発行タスクの準タスク部として実行される。
イベント処理関数
tk_evt_ssy の呼出によって呼び出される。
サブシステムに対する各種要求を処理する。
なお、すべての要求がすべてのサブシステムで処理されなければならないという性質のものではない。処理する必要がなければ何もせず単に E_OK を返せばよい。
イベント処理関数は次の形式となる。
ER eventfn( INT evttyp, ID resid, INT info ) { /* 各イベントに対応する処理 */ return ercd; }
evttyp
は要求の種別、resid
は対象となるリソースグループのID、info
は任意のパラメータで、いずれも tk_evt_ssy で指定されたものである。特定のリソースグループを対象としない場合は、resid
=0となる。
正常に処理した場合は戻値に E_OK を返す。異常があった場合はエラーコード(負の値)を返す。
evttyp
には次のものがある。詳細は デバイス管理機能項μT-Kernel/SMの機能章 を参照のこと。
#define TSEVT_SUSPEND_BEGIN 1 /* デバイスサスペンド開始前 */ #define TSEVT_SUSPEND_DONE 2 /* デバイスサスペンド完了後 */ #define TSEVT_RESUME_BEGIN 3 /* デバイスリジューム開始前 */ #define TSEVT_RESUME_DONE 4 /* デバイスリジューム完了後 */ #define TSEVT_DEVICE_REGIST 5 /* デバイス登録通知 */ #define TSEVT_DEVICE_DELETE 6 /* デバイス抹消通知 */
イベント処理関数は、tk_evt_ssy の発行タスクの準タスク部として実行される。
拡張SVCハンドラおよびブレーク関数/スタートアップ関数/クリーンアップ関数/イベント関数は TA_HLNG 属性相当のみで、高級言語対応ルーチンを経由して呼び出される。TA_ASM 属性を指定する機能はない。
スタートアップ関数によりリソース管理ブロックが初期化される以前、およびクリーンアップ関数によりリソースが解放されたあとに、そのリソースグループに属するタスクから拡張SVCが呼び出された場合の動作は、サブシステムの実装に依存する。カーネルではこのような呼出を特に禁止しない。基本的には、スタートアップ関数の呼出前およびクリーンアップ関数の呼出後に、拡張SVCを呼び出さないようにする必要がある。
何らかの理由で、スタートアップ関数が呼び出されないまま、ブレーク関数/クリーンアップ関数/イベント処理関数が呼び出される場合がある。このような場合も、誤動作せずに実行されなければならない。リソース管理ブロックは、リソース管理ブロックが最初に生成された時および tk_cln_ssy によるクリーンアップ処理時に0クリアされる。したがって、スタートアップ関数により正常に初期化されていなければ、リソース管理ブロックは0クリアされているはずである。
拡張SVCハンドラ内での固有空間は、呼出側の固有空間と同じである。したがって、呼出側の渡したバッファにアクセスする際にも、固有空間を切り替える必要はない。ただし、拡張SVCハンドラは保護レベル0(特権モード)で実行されるので、呼出側のタスク等からは本来アクセスできないメモリ等にもアクセスが可能である。このため、拡張SVCハンドラの中では、必要に応じて、ChkSpaceR(), ChkSpaceRW() などを使ったアクセス権チェックを行わなければならない。
拡張SVCハンドラの中で待ち状態に入るシステムコールを発行しても構わないが、ブレーク関数による中止を考慮したプログラムにしておく必要がある。この場合の具体的な処理は次のようになる。拡張SVCハンドラの実行中に、呼出側のタスクを対象とした tk_ras_tex が発行された場合、できるだけ速やかに拡張SVCハンドラの実行中止処理を行い、呼出側に対して中止のエラーを返す必要がある。このためにブレーク関数が実行される。ブレーク関数の中では、速やかに実行中止処理を行うため、拡張SVCハンドラの処理中に待ち状態になっていた場合でも、その待ち状態を強制的に解除する必要がある。このためのシステムコールとして、通常は tk_dis_wai を使う。tk_dis_wai の機能により、その後も拡張SVCハンドラから呼出側に戻るまでの間は待ち状態に入らないようにできるが、拡張SVCハンドラ側のプログラムも、ブレーク関数による実行の中止を考慮しておくべきである。たとえば、待ち状態から E_DISWAI のエラーで抜けた場合には、ブレーク関数による実行の中止を意味すると考えられるので、その後に予定していた処理を行わず、速やかに拡張SVCハンドラを終了し、呼出側に対して中止のエラーを返すようにする。
拡張SVCハンドラは、複数のタスクから同時に並行して呼び出される可能性がある。そのため、共通に利用する資源等があった場合には、拡張SVCハンドラの中で、必要に応じて排他制御を行う。
T_DSSYのメンバresblksz
の型がINTからSZに変更されている。
T_DSSYのメンバresblksz
の型がWからSZに変更されている。
INT型が16ビット幅の環境では、機能コードのうちサブシステム内の機能コードとして使える上位ビットは7ビット分(0〜127)になるため注意が必要である。
ssid
のサブシステムのスタートアップ関数を呼び出す。
ssid
=0を指定したときは、現在定義されているすべてのサブシステムのスタートアップ関数を呼び出す。この場合、サブシステム優先度の高いものから順に各サブシステムのスタートアップ関数を呼び出す。
同一優先度の場合の呼出順は不定である。
したがって、複数のサブシステム間に依存関係がある場合、サブシステム優先度をその依存関係にしたがって設定する必要がある。例えば、サブシステムBがサブシステムAの機能を利用しているような場合、サブシステムAの優先度をサブシステムBより高くしなければならない。
スタートアップ関数が定義されていないサブシステムに対して実行しても、単にスタートアップ関数を呼び出さないだけでエラーとはならない。
スタートアップ関数を実行中に、tk_sta_ssy を呼び出したタスクにタスク例外が発生した場合、タスク例外はスタートアップ関数が終了するまで保留される。
T-Kernelの上位ミドルウェアであるT-Kernel Extension(T-Kernel Standard Extension)では、プロセス生成時(起動時)のスタートアップ処理とプロセス終了時のクリーンアップ処理を行うために、tk_sta_ssy および tk_cln_ssy を利用する。具体的には、T-Kernel Extensionの中のプロセスの生成時(起動時)の処理において、ssid
=0を指定した tk_sta_ssy を発行し、新しく起動されたプロセスに対するスタートアップ処理を行う。また、T-Kernel Extensionの中のプロセス終了時の処理において、ssid
=0を指定した tk_cln_ssy を発行し、終了するプロセスに対するクリーンアップ処理を行う。たとえば、ファイル管理のサブシステムがこの機能を利用することにより、プロセス終了時に実行されるファイル管理サブシステムのクリーンアップ処理の中で、そのプロセスがオープンしていたファイルを自動的にクローズすることができる。
複数のサブシステムが定義されていた場合は、各サブシステムに対するスタートアップ関数やクリーンアップ関数が順に実行されるが、その実行順序はサブシステム優先度によって決められ、スタートアップ処理とクリーンアップ処理では逆の実行順序になる。
たとえば、サブシステムBの実装のために別のサブシステムAを使っているケースでは、サブシステムAの優先度をサブシステムBの優先度よりも高くしておく。そうすると、新しく起動するプロセスに対して、サブシステムAのスタートアップ処理がサブシステムBのスタートアップ処理よりも先に実行される。したがって、サブシステムBのスタートアップ処理の中で、サブシステムAの機能(拡張SVCハンドラ)を呼び出すことができる。また、終了するプロセスに対しては、サブシステムBのクリーンアップ処理がサブシステムAのクリーンアップ処理よりも先に実行される。したがって、サブシステムBのクリーンアップ処理の中で、サブシステムAの機能(拡張SVCハンドラ)を呼び出すことができる[図16]。
すべてのサブシステムのスタートアップ関数は、新しいプロセスが生成(起動)される度に必ず実行される。しかし、起動されたプロセスがそのサブシステムの機能を利用するとは限らず、一度もそのサブシステムを呼び出さないかもしれない。サブシステムとは無関係のプロセスも含めて、プロセスの生成時(起動時)に各サブシステムのスタートアップ関数がすべて実行されることを考えると、スタートアップ関数によるオーバーヘッドは最小限としておくべきである。すなわち、スタートアップ関数の中では必要最小限の処理のみを行うようにし、複雑な処理を行う必要があれば、スタートアップ関数の中ではなく、実際にサブシステムが利用された時、たとえば最初にそのプロセスから拡張SVCハンドラが呼ばれた時までその処理を遅らせる方がよい。
info
がINT型であり、処理系によってとれる値の範囲に異なる可能性があるため注意が必要である。
ssid
のサブシステムのクリーンアップ関数を呼び出す。
ssid
=0を指定したときは、現在定義されているすべてのサブシステムのクリーンアップ関数を呼び出す。この場合、サブシステム優先度の低いものから順に各サブシステムのクリーンアップ関数を呼び出す。
同一優先度の場合の呼出順は不定である。
したがって、複数のサブシステム間に依存関係がある場合、サブシステム優先度をその依存関係にしたがって設定する必要がある。例えば、サブシステムBがサブシステムAの機能を利用しているような場合、サブシステムAの優先度をサブシステムBより高くしなければならない。
クリーンアップ関数が定義されていないサブシステムに対して実行しても、単にクリーンアップ関数を呼び出さないだけでエラーとはならない。
クリーンアップ関数を実行中に、tk_cln_ssy を呼び出したタスクにタスク例外が発生した場合、タスク例外はクリーンアップ関数が終了するまで保留される。
info
がINT型であり、処理系によってとれる値の範囲に異なる可能性があるため注意が必要である。
以下のサービスプロファイルが有効に設定されている場合に限り、本システムコールはサポートされる。
ssid
のサブシステムのイベント処理関数を呼び出す。
ssid
=0を指定したときは、現在定義されているすべてのサブシステムのイベント処理関数を呼び出す。この場合、次の順序で各サブシステムのイベント処理関数を呼び出す。
evttyp
が奇数のとき:サブシステム優先度の高い方から順に呼び出す。
evttyp
が偶数のとき:サブシステム優先度の低い方から順に呼び出す。
同一優先度の場合の呼出順は不定である。
イベント処理関数が定義されていないサブシステムに対して実行しても、単にイベント処理関数を呼び出さないだけでエラーとはならない。
特定のリソースに対する処理要求でない場合は、resid
=0とする。
イベント処理関数がエラーを返した場合、システムコールの戻値としてそのエラー値をそのまま返す。ssid
=0の場合、イベント処理関数がエラーを返した場合も、すべてのサブシステムのイベント処理関数が呼び出される。システムコールの戻値には、エラーを返したイベント処理関数の内の1つのエラー値のみ返す。どのサブシステムのイベント処理関数が返したエラーかはわからない。
イベント処理関数を実行中に、tk_evt_ssy を呼び出したタスクにタスク例外が発生した場合、タスク例外はイベント処理関数が終了するまで保留される。
イベント処理関数の利用例の1つは、省電力機能に関連したサスペンドやリジュームの処理である。具体的には、電源異常などの要因により電源を切った状態(デバイスのサスペンド状態)に移行する際、サスペンド状態への移行を各サブシステムに対して通知し、サブシステム毎に適切な処理を行うために、各サブシステムのイベント処理関数が呼び出される。T-Kernel/SMの tk_sus_dev の処理の中では、この目的で tk_evt_ssy を実行している。各サブシステムのイベント処理関数では、必要に応じて、サスペンド状態への移行時に処理すべきデータの保存などを行う。一方、電源の再投入などによってサスペンド状態から復帰(リジューム)する際には、サスペンド状態からの復帰を各サブシステムに対して通知し、サブシステム毎に適切な処理を行うために、各サブシステムのイベント処理関数が再度呼び出される。詳細は tk_sus_dev を参照。
このほか、tk_def_dev によって新しいデバイスが登録された際にも、デバイスの登録を各サブシステムに対して通知し、サブシステム毎に適切な処理を行うために、各サブシステムのイベント処理関数が呼び出される。T-Kernel/SMの tk_def_dev の処理の中では、この目的で tk_evt_ssy を実行している。
info
がINT型であり、処理系によってとれる値の範囲に異なる可能性があるため注意が必要である。
pk_rssy
の内容
以下のいずれかのサービスプロファイルが有効に設定されているとき、サブシステム優先度(ssypri
)の取得が可能である。
ssid
で示された対象サブシステムの各種の情報を参照する。
ssypri
には、tk_def_ssy で指定したサブシステムの優先度が返される。
resblksz
には、tk_def_ssy で指定したリソース管理ブロックサイズが返される。
ssid
のサブシステムが定義されていない場合は、E_NOEXS となる。
T_RSSYのメンバresblksz
の型がINTからSZに変更されている。
T_RSSYのメンバresblksz
の型がWからSZに変更されている。
なし
新規のリソースグループを生成し、リソース管理ブロックとリソースIDを割り当てる。
リソースIDは全サブシステムに対して共通で割り当てられ、リソース管理ブロックが各サブシステムごとに生成される[図17]。
すでにリソースグループが生成されている状態で、新たにサブシステムが定義される場合がある。この場合も、新たに登録されたサブシステムに対して、すでに存在するリソースグループのリソース管理ブロックが生成されなければならない。つまり、リソース管理ブロックの生成は tk_def_ssy でも行わなければならないケースがありうる。
例えば、[図17]のような状態のとき新たにサブシステムDが定義された場合、自動的にサブシステムDのためのリソースID #1,#2,#3のリソース管理ブロックが生成されなければならない。
リソースIDは論理空間ID(lsid
)としても使用される場合がある。そのため、リソースIDは論理空間IDとして直接利用可能な値、もしくは簡単な変換で論理空間IDとして利用可能な値とすることが望ましい。
特殊なリソースグループとして、システムリソースグループが必ず存在する。システムリソースグループは、tk_cre_res で生成するまでもなくシステム起動時から必ず1つ存在しているリソースグループである。システムリソースグループは削除することはできない。必ず存在していることを除けば、システムリソースグループが他のリソースグループと異なる部分はない。
リソース管理ブロックの生成は、次の2種類の実装が考えられる。
(A) サブシステム定義時(tk_def_ssy)に最大リソースグループ数分のリソース管理ブロックを生成しておき、tk_cre_res では単にそれを割り当てる。
(B) tk_cre_res で全サブシステム分のリソース管理ブロックを生成して、それを割り当てる。
最初にリソース管理ブロックを生成した時に、リソース管理ブロックを0クリアするという仕様としているため、(A)と(B)ではリソース管理ブロックの0クリアのタイミングが異なる。これによる影響は少ないと考えられるが、(A)の方が0クリアされるケースが少なくなるため、サブシステムは(A)を前提として実装しなければならない。なお、カーネルの実装としても(A)を推奨する。
T-Kernelの上位ミドルウェアであるT-Kernel Extension(T-Kernel Standard Extension)では、T-Kernelのリソースグループの機能を使ってプロセスの機能を実現しており、1つのプロセスが1つのリソースグループに対応する。このため、プロセスの生成時(起動時)には、tk_cre_res を実行して、プロセス毎のリソース管理ブロックを確保する。
リソース管理の機能により、プロセス単位(リソースグループ単位)で独立したリソースを各サブシステムに持たせたり、プロセス終了時に自動的にリソースを解放するなどの機能が実現できる。たとえば、ファイルを管理するサブシステムでは、プロセスがファイルをオープンする度に、ファイルディスクリプタと呼ばれるファイル操作用の識別子が割り当てられ、以後のファイル操作には、ファイルディスクリプタを用いる場合が多い。この場合、ファイルディスクリプタによって識別される、ファイル操作用の各種の管理情報がリソースとなる。このリソースを、ファイル管理サブシステムに対するリソース管理ブロック内に置くことにより、ファイル操作用の各種の情報をプロセス単位(リソースグループ単位)で独立に管理することができる。
一般に、プロセス毎に独立して管理すべき機能を実現するサブシステムでは、サブシステムに対する各プロセスの情報を独立して管理するために、リソース管理ブロックの利用が有効である。また、新しく生成(起動)されたプロセスに対するサブシステム側の準備やリソース管理ブロックの初期化のために、サブシステムのスタートアップ関数を利用したり、プロセス終了時の自動的なリソース解放のために、サブシステムのクリーンアップ関数を利用できる。一方、プロセスとの直接的な関連を持たない機能(どのプロセスからも区別されずに共有される機能、システム全体にかかわる機能など)を実現するサブシステムの場合は、リソース管理ブロックやリソース、リソースグループに関する機能を使う機会は少ない。
新しいプロセスを生成(起動)した際には、そのプロセスが各サブシステムの機能を実際に使うかどうかに関わらず、システム共有空間の常駐メモリ領域の中に、各サブシステムに対するリソース管理ブロックが確保される。すなわち、システム共通の資源を消費することになる。したがって、システム全体に対するオーバーヘッドを減らすには、リソース管理ブロックのサイズを最小限としておくのが望ましい。
たとえば、あるサブシステムにおいて、プロセス毎に独立した作業用メモリが1MBずつ必要であるとする。プロセス毎に必要であるという点から、リソース管理ブロックの一部を作業用メモリとして利用する方法が考えられるが、この容量はリソース管理ブロックとしては大きすぎる。リソース管理ブロックのサイズを1MBとした場合、新しいプロセスを生成(起動)する度に、これだけの容量のメモリが無条件に確保されることになり、システム共有空間の常駐メモリを消費し過ぎるからである。特に、新しいプロセスがこのサブシステムの機能を全く使わない場合には、メモリの無駄が大きい。
このようなケースでは、サブシステムの使う作業用メモリの確保を、実際の必要時まで遅らせる方がよい。たとえば、作業用メモリが確保できているかどうかのフラグと、作業用メモリのアドレスのみをリソース管理ブロックに入れておき、プロセスがサブシステムの機能を使った場合(拡張SVCハンドラを呼び出した場合)には、そのフラグをチェックして、作業用メモリがまだ確保されていない場合にのみメモリの確保を行うようにする。このような処理を行えば、サブシステムとは関係のないプロセスにも大きなリソース管理ブロックを割り当てるといった無駄がなくなる。
削除対象のリソースに属しているタスクが残っていても、リソースは削除される。原則として、削除対象のリソースに属しているすべてのタスクを終了・削除した後に、リソースを削除しなければならない。削除対象リソースに属しているタスクが残っている状態で、かつそのタスクがサブシステム(拡張SVC)を呼び出している状態で、リソースを削除した場合の動作は保証されない。また、削除されたリソースに属するタスクが、サブシステム(拡張SVC)を呼び出した場合の動作も保証されない。
リソース管理ブロックが実際に削除されるタイミングは実装に依存する。(tk_cre_res 参照)
システムリソースグループの削除はできない。(E_ID)
ssid
のサブシステムの resid
のリソース管理ブロックのアドレスを取得する。
削除されたリソースIDに対しても E_OK となる場合がある。エラー(E_NOEXS)が返されるか否かは実装依存である。