サブシステム管理機能は、μT-Kernel上で動作するミドルウェア等を実装するために、「サブシステム」と呼ばれるユーザ定義の機能をカーネルに追加し、μT-Kernel本体の機能を拡張するための機能である。μT-Kernel/SMの一部の機能についても、サブシステム管理機能を利用して実装されている。
サブシステムは、ユーザ定義のシステムコール(「拡張SVC」と呼ぶ)を実行するための拡張SVCハンドラのほか、例外発生時の処理を行うブレーク関数、デバイス等からのイベント発生時の処理を行うイベント処理関数から構成される(図10)。
このうち、拡張SVCハンドラはアプリケーションなどからの要求を直接受け付けて処理する。一方、ブレーク関数、イベント処理関数は、いわゆるコールバック型の関数であり、カーネルからの要求を受け付けて処理する。
補足事項 | |
---|---|
一般に、プロセス管理機能、ファイル管理機能などを含む上位ミドルウェアの機能を実装する場合には、サブシステム管理機能を利用する。このほか、サブシステム管理機能を利用して実装されたミドルウェアの例として、TCP/IPマネージャ、USBマネージャ、PCカードマネージャなどがある。 サブシステム管理機能は、大まかに言えばシステムコール(SVC: SuperVisor Call)をユーザが追加するための機能であるが、単なるユーザ定義システムコールの追加だけではなく、追加したシステムコールの処理に付随して必要となる例外発生時の処理機能も提供することによって、高度で複雑な機能を持つミドルウェアを実現可能としたものである。 なお、μT-Kernel本体の機能を拡張するための機能としては、サブシステム管理機能のほかに、デバイスドライバの機能がある。サブシステムもデバイスドライバも、μT-Kernel本体とは独立した機能モジュールであり、対応するバイナリプログラムをロードすることによって、μT-Kernel上のタスクからこれらの機能を呼び出して利用できるようになる。また、両者とも保護レベル0で動作する。一方、両者の相違点としては、デバイスドライバを呼び出す際のAPIがオープン/クローズ、リード/ライト型に固定されているのに対して、サブシステムを呼び出す際のAPIは自由に定義できる。 サブシステムは、サブシステムID( |
pk_dssy
の内容
以下のサービスプロファイルが有効に設定されている場合に限り、本システムコールはサポートされる。
以下のサービスプロファイルが有効に設定されているとき、サブシステム優先度(ssypri
)の指定が可能である。
また、以下のサービスプロファイルが有効に設定されているとき、ブレーク関数の指定が可能である。
ssid
のサブシステムを定義する。
1つのサブシステムに1つのサブシステムIDを、他のサブシステムと重複しないように割り当てなければならない。カーネルに自動割当ての機能はない。
サブシステムIDは1~9がμT-Kernel用に予約されている。10~255がミドルウェア等で使用できる番号となる。ただし、使用可能なサブシステムIDの最大値は実装定義であり、255より小さい場合がある。
ssyatr
は、下位側がシステム属性を表し、上位側が実装独自属性を表す。ssyatr
のシステム属性には、現在のバージョンでは割当てがなく、システム属性は使われていない。
ssypri
は、サブシステムの優先度で、スタートアップ関数、クリーンアップ関数、イベント処理関数が優先度順に呼び出される。同一優先度の場合の呼出順は不定である。サブシステム優先度は、1が最も優先度が高く、数値が大きくなるにしたがって優先度が下がる。指定できる優先度の範囲は実装定義だが、少なくとも1~16が指定できなければならない。
breakfn
、eventfn
はそれぞれ NULL を指定することができる。NULL を指定した場合、対応する関数は呼び出されない。
pk_dssy
=NULL とした場合は、サブシステムの定義を抹消する。
拡張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_evt_ssy の呼出によって呼び出される。
サブシステムに対する各種要求を処理する。
なお、すべての要求がすべてのサブシステムで処理されなければならないという性質のものではない。処理する必要がなければ何もせず単に E_OK を返せばよい。
イベント処理関数は次の形式となる。
ER eventfn( INT evttyp, INT info ) { /* 各イベントに対応する処理 */ return ercd; }
evttyp
は要求の種別、info
は任意のパラメータで、いずれも tk_evt_ssy で指定されたものである。
正常に処理した場合は戻値に 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_ras_tex が発行された場合、できるだけ速やかに拡張SVCハンドラの実行中止処理を行い、呼出側に対して中止のエラーを返す必要がある。このためにブレーク関数が実行される。ブレーク関数の中では、速やかに実行中止処理を行うため、拡張SVCハンドラの処理中に待ち状態になっていた場合でも、その待ち状態を強制的に解除する必要がある。このためのシステムコールとして、通常は tk_dis_wai を使う。tk_dis_wai の機能により、その後も拡張SVCハンドラから呼出側に戻るまでの間は待ち状態に入らないようにできるが、拡張SVCハンドラ側のプログラムも、ブレーク関数による実行の中止を考慮しておくべきである。たとえば、待ち状態から E_DISWAI のエラーで抜けた場合には、ブレーク関数による実行の中止を意味すると考えられるので、その後に予定していた処理を行わず、速やかに拡張SVCハンドラを終了し、呼出側に対して中止のエラーを返すようにする。
拡張SVCハンドラは、複数のタスクから同時に並行して呼び出される可能性がある。そのため、共通に利用する資源等があった場合には、拡張SVCハンドラの中で、必要に応じて排他制御を行う。
INT型が16ビット幅の環境では、機能コードのうちサブシステム内の機能コードとして使える上位ビットは7ビット分(0〜127)になるため注意が必要である。
以下のすべてのサービスプロファイルが有効に設定されている場合に限り、本システムコールはサポートされる。
ssid
のサブシステムのイベント処理関数を呼び出す。
ssid
=0を指定したときは、現在定義されているすべてのサブシステムのイベント処理関数を呼び出す。この場合、次の順序で各サブシステムのイベント処理関数を呼び出す。
evttyp
が奇数のとき:サブシステム優先度の高い方から順に呼び出す。
evttyp
が偶数のとき:サブシステム優先度の低い方から順に呼び出す。
同一優先度の場合の呼出順は不定である。
イベント処理関数が定義されていないサブシステムに対して実行しても、単にイベント処理関数を呼び出さないだけでエラーとはならない。
イベント処理関数がエラーを返した場合、システムコールの戻値としてそのエラー値をそのまま返す。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 で指定したサブシステムの優先度が返される。
ssid
のサブシステムが定義されていない場合は、E_NOEXS となる。