拡張同期・通信機能は、タスクとは独立したオブジェクトにより、タスク間の高度な同期・通信を行うための機能である。ミューテックス、メッセージバッファ、ランデブの各機能が含まれる。
ミューテックスは、共有資源を使用する際にタスク間で排他制御を行うためのオブジェクトである。ミューテックスは、排他制御に伴う上限のない優先度逆転を防ぐための機構として、優先度継承プロトコル(priority inheritance protocol)と優先度上限プロトコル(priority ceiling protocol)をサポートする。
ミューテックス機能には、ミューテックスを生成/削除する機能、ミューテックスをロック/ロック解除する機能、ミューテックスの状態を参照する機能が含まれる。ミューテックスはID番号で識別されるオブジェクトである。ミューテックスのID番号をミューテックスIDと呼ぶ。
ミューテックスは、ロックされているかどうかの状態と、ロックを待つタスクの待ち行列を持つ。また、カーネルは、各ミューテックスに対してそれをロックしているタスクを、各タスクに対してそれがロックしているミューテックスの集合を管理する。タスクは、資源を使用する前に、ミューテックスをロックする。ミューテックスが他のタスクにロックされていた場合には、ミューテックスがロック解除されるまで、ミューテックスのロック待ち状態となる。ミューテックスのロック待ち状態になったタスクは、そのミューテックスの待ち行列につながれる。タスクは、資源の使用を終えると、ミューテックスのロックを解除する。
ミューテックスは、ミューテックス属性に TA_INHERIT(=0x02)を指定することにより優先度継承プロトコルを、TA_CEILING(=0x03)を指定することにより優先度上限プロトコルをサポートする。TA_CEILING 属性のミューテックスに対しては、そのミューテックスをロックする可能性のあるタスクの中で最も高いベース優先度を持つタスクのベース優先度を、ミューテックス生成時に上限優先度として設定する。TA_CEILING 属性のミューテックスを、その上限優先度よりも高いベース優先度を持つタスクがロックしようとした場合、E_ILUSE エラーとなる。また、TA_CEILING 属性のミューテックスをロックしているかロックを待っているタスクのベース優先度を、tk_chg_pri によってそのミューテックスの上限優先度よりも高く設定しようとした場合、tk_chg_pri が E_ILUSE エラーを返す。
これらのプロトコルを用いた場合、上限のない優先度逆転を防ぐために、ミューテックスの操作に伴ってタスクの現在優先度が自動的に変更される。優先度継承プロトコルと優先度上限プロトコルに厳密に従うなら、タスクの現在優先度を、次に挙げる優先度の最高値に常に一致するように変更する必要がある。これを、厳密な優先度制御規則と呼ぶ。
タスクのベース優先度
タスクが TA_INHERIT 属性のミューテックスをロックしている場合、それらのミューテックスのロックを待っているタスクの中で、最も高い現在優先度を持つタスクの現在優先度
タスクが TA_CEILING 属性のミューテックスをロックしている場合、それらのミューテックス中で、最も高い上限優先度を持つミューテックスの上限優先度
ここで、TA_INHERIT 属性のミューテックスを待っているタスクの現在優先度が、ミューテックス操作か tk_chg_pri によるベース優先度の変更に伴って変更された場合、そのミューテックスをロックしているタスクの現在優先度の変更が必要になる場合がある。これを推移的な優先度継承と呼ぶ。さらにそのタスクが、別の TA_INHERIT 属性のミューテックスを待っていた場合には、そのミューテックスをロックしているタスクに対して推移的な優先度継承の処理が必要になる場合がある。
T-Kernelでは、上述の厳密な優先度制御規則に加えて、現在優先度を変更する状況を限定した優先度制御規則(これを簡略化した優先度制御規則と呼ぶ)を規定し、どちらを採用するかは実装定義とする。具体的には、簡略化した優先度制御規則においては、タスクの現在優先度を高くする方向の変更はすべて行うのに対して、現在優先度を低くする方向の変更は、タスクがロックしているミューテックスがなくなった時にのみ行う(この場合には、タスクの現在優先度をベース優先度に戻すことになる)。より具体的には、次の状況でのみ現在優先度を変更する処理を行えばよい。
タスクがロックしている TA_INHERIT 属性のミューテックスを、そのタスクよりも高い現在優先度を持つタスクが待ち始めた時
タスクAによってロックされている TA_INHERIT 属性のミューテックスを待っている別のタスクBが、タスクAよりも高い現在優先度に変更された時
タスクが、そのタスクの現在優先度よりも高い上限優先度を持つ TA_CEILING 属性のミューテックスをロックした時
タスクがロックしているミューテックスがなくなった時
ミューテックスの操作に伴ってタスクの現在優先度を変更した場合には、次の処理を行う。
優先度を変更されたタスクが実行できる状態である場合、タスクの優先順位を、変更後の優先度にしたがって変化させる。変更後の優先度と同じ優先度を持つタスクの間での優先順位は、実装依存である。優先度が変更されたタスクが何らかのタスク優先度順の待ち行列につながれている場合にも、その待ち行列の中での順序を、変更後の優先度にしたがって変化させる。変更後の優先度と同じ優先度を持つタスクの間での順序は、実装依存である。タスクが終了する時に、そのタスクがロックしているミューテックスが残っている場合には、それらのミューテックスをすべてロック解除する。ロックしているミューテックスが複数ある場合には、それらをロック解除する順序は実装依存である。ロック解除の具体的な処理内容については、tk_unl_mtx の機能説明を参照すること。
補足事項 | |
---|---|
TA_TFIFO 属性または TA_TPRI 属性のミューテックスは、最大資源数が1のセマフォ(バイナリセマフォ)と同等の機能を持つ。ただし、ミューテックスは、ロックしたタスク以外はロック解除できない、タスク終了時に自動的にロック解除されるなどの違いがある。 ここでいう優先度上限プロトコルは、広い意味での優先度上限プロトコルで、最初に優先度上限プロトコルとして提案されたアルゴリズムではない。厳密には、highest locker protocolなどと呼ばれているアルゴリズムである。 ミューテックスの操作に伴ってタスクの現在優先度を変更した結果、優先度を変更されたタスクのタスク優先度順の待ち行列の中での順序が変化した場合、優先度を変更されたタスクないしはその待ち行列で待っている他のタスクの待ち解除が必要になる場合がある。 |
仕様決定の理由 | |
---|---|
ミューテックスの操作に伴ってタスクの現在優先度を変更した場合に、変更後の優先度と同じ優先度を持つタスクの間での優先順位を実装依存としたのは、次の理由による。アプリケーションによっては、ミューテックス機能による現在優先度の変更が頻繁に発生する可能性があり、それに伴ってタスク切替えが多発するのは望ましくない(同じ優先度を持つタスクの間での優先順位を最低とすると、不必要なタスク切替えが起こる)。理想的には、タスクの優先度ではなく優先順位を継承するのが望ましいが、このような仕様にすると実装上のオーバヘッドが大きくなるため、実装依存とすることにした。 |
pk_cmtx
の内容
ミューテックスを生成しミューテックスID番号を割り当てる。具体的には、生成するミューテックスに対して管理ブロックなどを割り付ける。
exinf
は、対象ミューテックスに関する情報を入れておくためにユーザが自由に利用できる。ここで設定した情報は、tk_ref_mtx で取り出すことができる。なお、ユーザの情報を入れるためにもっと大きな領域がほしい場合や、途中で内容を変更したい場合には、自分でそのためのメモリを確保し、そのメモリパケットのアドレスを exinf
に入れる。カーネルでは exinf
の内容について関知しない。
mtxatr
は、下位側がシステム属性を表し、上位側が実装独自属性を表す。mtxatr
のシステム属性の部分では、次のような指定を行う。
mtxatr:= (TA_TFIFO || TA_TPRI || TA_INHERIT || TA_CEILING) | [TA_DSNAME] | [TA_NODISWAI]
TA_TFIFO | 待ちタスクのキューイングはFIFO |
TA_TPRI | 待ちタスクのキューイングは優先度順 |
TA_INHERIT | 優先度継承プロトコル |
TA_CEILING | 優先度上限プロトコル |
TA_DSNAME | DSオブジェクト名称を指定する |
TA_NODISWAI | tk_dis_wai による待ち禁止を拒否する |
TA_TFIFO の場合、ミューテックスのタスクの待ち行列はFIFOとなる。TA_TPRI, TA_INHERIT, TA_CEILING では、タスクの優先度順となる。TA_INHERIT では優先度継承プロトコル、TA_CEILING では優先度上限プロトコルが適用される。
TA_CEILING の場合のみ ceilpri
が有効となり、ミューテックスの上限優先度を設定する。
TA_DSNAME を指定した場合に dsname
が有効となり、DSオブジェクト名称として設定される。DSオブジェクト名称はデバッガがオブジェクトを識別するために使用され、T-Kernel/DSのシステムコール td_ref_dsname と td_set_dsname からのみ操作可能である。詳細は td_ref_dsname 、td_set_dsname を参照のこと。TA_DSNAME を指定しなかった場合は、dsname
が無視され、td_ref_dsname や td_set_dsname が、E_OBJ エラーとなる。
#define TA_TFIFO 0x00000000 /* 待ちタスクをFIFOで管理 */ #define TA_TPRI 0x00000001 /* 待ちタスクを優先度順で管理 */ #define TA_INHERIT 0x00000002 /* 優先度継承プロトコル */ #define TA_CEILING 0x00000003 /* 優先度上限プロトコル */ #define TA_DSNAME 0x00000040 /* DSオブジェクト名称を指定 */ #define TA_NODISWAI 0x00000080 /* 待ち禁止拒否 */
mtxid
で示されたミューテックスを削除する。
本システムコールの発行により、対象ミューテックスのID番号および管理ブロック用の領域は解放される。
対象ミューテックスにおいてロック待ちしているタスクがあった場合にも、本システムコールは正常終了するが、待ち状態にあったタスクにはエラー E_DLT が返される。
ミューテックスが削除されると、そのミューテックスをロックしているタスクにとっては、ロックしているミューテックスが減ることになる。したがって、削除されるミューテックスが TA_INHERIT または TA_CEILING 属性の場合には、ロックしていたタスクの優先度が変更される場合がある。
E_OK | 正常終了 |
E_ID | 不正ID番号(mtxid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mtxid のミューテックスが存在しない) |
E_PAR | パラメータエラー(tmout ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ミューテックスが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
E_ILUSE | 不正使用(多重ロック、上限優先度違反) |
mtxid
のミューテックスをロックする。ミューテックスがロックできれば、本システムコールの発行タスクは待ち状態に入らず、実行を継続する。この場合、そのミューテックスはロック状態になる。ロックできなければ、本システムコールを発行したタスクは待ち状態に入る。すなわち、そのミューテックスに対する待ち行列につながれる。
tmout
により待ち時間の最大値(タイムアウト値)を指定することができる。待ち解除の条件が満足されない(ロックが解除されない)まま tmout
の時間が経過すると、タイムアウトエラー E_TMOUT となってシステムコールが終了する。
tmout
としては、正の値のみを指定することができる。tmout
の基準時間(時間の単位)はシステム時刻の基準時間(=1ミリ秒)と同じである。
tmout
として TMO_POL=0を指定した場合は、タイムアウト値として0を指定したことを示し、ロックできなくても待ちに入らず E_TMOUT を返す。また、tmout
として TMO_FEVR=(-1)を指定した場合は、タイムアウト値として無限大の時間を指定したことを示し、タイムアウトせずにロックできるまで待ち続ける。
自タスクがすでに対象ミューテックスをロックしていた場合には、E_ILUSE(多重ロック)を返す。
対象ミューテックスが TA_CEILING 属性の場合、自タスクのベース優先度[1]が対象ミューテックスの上限優先度より高い場合には E_ILUSE(上限優先度違反)を返す。
TA_INHERIT 属性のミューテックスの場合
自タスクがロック待ち状態になる場合、そのミューテックスをロックしているタスクの現在優先度が自タスクより低ければ、ロックしているタスクの優先度を自タスクと同じ優先度まで引き上げる。ロックを待っているタスクがロックを獲得せずに待ちを終了した場合(タイムアウトなど)、そのミューテックスをロック中のタスクの優先度を、次の内の最も高い優先度まで引き下げる。ただし、この優先度の引き下げを行うか否かは実装依存である。
そのミューテックスでロック待ちしているタスクの現在優先度の内の最も高い優先度。
そのミューテックスをロック中のタスクがロックしている他のすべてのミューテックスの内の最も高い優先度。
ロック中のタスクのベース優先度。
TA_CEILING 属性のミューテックスの場合
自タスクがロックを獲得した場合、自タスクの現在優先度がミューテックスの上限優先度より低ければ、自タスクの優先度をミューテックスの上限優先度まで引き上げる。
E_OK | 正常終了 |
E_ID | 不正ID番号(mtxid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mtxid のミューテックスが存在しない) |
E_PAR | パラメータエラー(tmout_u ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ミューテックスが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
E_ILUSE | 不正使用(多重ロック、上限優先度違反) |
tk_loc_mtx のパラメータである tmout
を64ビットマイクロ秒単位の tmout_u
としたシステムコールである。
パラメータが tmout_u
となった点を除き、本システムコールの仕様は tk_loc_mtx と同じである。詳細は tk_loc_mtx の説明を参照のこと。
T-Kernel 2.0で追加されたシステムコールである。
mtxid
のミューテックスのロックを解除する。
ロック待ちしているタスクがあれば、待ち行列の先頭のタスクの待ちを解除し、そのタスクをロック獲得状態にする。
自タスクがロックしていないミューテックスを指定した場合、E_ILUSE を返す。
ロック解除したミューテックスが TA_INHERIT または TA_CEILING 属性の場合、次のようにタスク優先度を引き下げる必要がある。
ロックを解除することにより、自タスクがロックしているミューテックスがすべてなくなった場合は、自タスクの優先度をベース優先度まで引き下げる。
自タスクがロック中のミューテックスが残っている場合、自タスクの優先度を次の内の最も高い優先度まで引き下げる。
自タスクがロックしている TA_INHERIT 属性を持つミューテックスの待ち行列につながれているタスクの現在優先度の中で最も高い優先度
自タスクがロックしている TA_CEILING 属性を持つミューテックスに設定されている上限優先度の中で最も高い優先度
自タスクのベース優先度
ただし、ロック中のミューテックスが残っている場合の優先度の引き下げを行うか否かは実装依存である。
ミューテックスをロックした状態でタスクを終了した(休止状態(DORMANT)または未登録状態(NON-EXISTENT)になった)場合、当該タスクがロックしているすべてのミューテックスはT-Kernelによって自動的にロック解除される。
pk_rmtx
の内容
mtxid
で示された対象ミューテックスの各種の状態を参照し、リターンパラメータとしてロック中のタスク(htsk
)、ロック待ちタスク(wtsk
)、拡張情報(exinf
) を返す。
htsk
は、このミューテックスをロックしているタスクのIDを示す。ロックしているタスクがない場合は htsk
=0となる。
wtsk
は、このミューテックスで待っているタスクのIDを示す。複数のタスクが待っている場合には、待ち行列の先頭のタスクのIDを返す。待ちタスクが無い場合は wtsk
=0となる。
対象ミューテックスが存在しない場合には、エラー E_NOEXS となる。
メッセージバッファは、可変長のメッセージを受渡しすることにより、同期と通信を行うためのオブジェクトである。メッセージバッファ機能には、メッセージバッファを生成/削除する機能、メッセージバッファに対してメッセージを送信/受信する機能、メッセージバッファの状態を参照する機能が含まれる。メッセージバッファはID番号で識別されるオブジェクトである。メッセージバッファのID番号をメッセージバッファIDと呼ぶ。
メッセージバッファは、メッセージの送信を待つタスクの待ち行列(送信待ち行列)とメッセージの受信を待つタスクの待ち行列(受信待ち行列)を持つ。また、送信されたメッセージを格納するためのメッセージバッファ領域を持つ。メッセージを送信する側(イベントを知らせる側)では、送信したいメッセージをメッセージバッファにコピーする。メッセージバッファ領域の空き領域が足りなくなった場合、メッセージバッファ領域に十分な空きができるまでメッセージバッファへの送信待ち状態になる。
メッセージバッファへの送信待ち状態になったタスクは、そのメッセージバッファの送信待ち行列につながれる。一方、メッセージを受信する側(イベントを待つ側)では、メッセージバッファに入っているメッセージを一つ取り出す。メッセージバッファにメッセージが入っていない場合は、次にメッセージが送られてくるまでメッセージバッファからの受信待ち状態になる。メッセージバッファからの受信待ち状態になったタスクは、そのメッセージバッファの受信待ち行列につながれる。
メッセージバッファ領域のサイズを0にすることで、同期メッセージ機能を実現することができる。すなわち、送信側のタスクと受信側のタスクが、それぞれ相手のタスクがシステムコールを呼び出すのを待ち合わせ、両者がシステムコールを呼び出した時点で、メッセージの受渡しが行われる。
補足事項 | |
---|---|
メッセージバッファ領域のサイズを0にした場合の、メッセージバッファの動作を[図3]の例を用いて説明する。この図で、タスクAとタスクBは非同期に実行しているものとする。
メッセージバッファへの送信を待っているタスクは、待ち行列につながれている順序でメッセージを送信する。例えば、あるメッセージバッファに対して40バイトのメッセージを送信しようとしているタスクAと、10バイトのメッセージを送信しようとしているタスクBが、この順で待ち行列につながれている時に、別のタスクによるメッセージの受信により20バイトの空き領域ができたとする。このような場合でも、タスクAがメッセージを送信するまで、タスクBはメッセージを送信できない。 メッセージバッファは、可変長のメッセージをコピーして受渡しする。メールボックスとの違いは、メッセージをコピーすることである。 メッセージバッファは、リングバッファで実装することを想定している。 |
pk_cmbf
の内容
メッセージバッファを生成しメッセージバッファID番号を割り当てる。具体的には、生成するメッセージバッファに対して管理ブロックを割り付ける。また、bufsz
の情報を元に、メッセージキュー(受信されるのを待つメッセージの待ち行列) として利用するためのリングバッファの領域を確保する。
メッセージバッファは、可変長メッセージの送受信の管理を行うオブジェクトである。メールボックス(mbx)との違いは、送信時と受信時に可変長のメッセージ内容がコピーされるということである。また、バッファが一杯の場合に、メッセージ送信側も待ち状態に入る機能がある。
exinf
は、対象メッセージバッファに関する情報を入れておくためにユーザが自由に利用できる。ここで設定した情報は、tk_ref_mbf で取り出すことができる。なお、ユーザの情報を入れるためにもっと大きな領域がほしい場合や、途中で内容を変更したい場合には、自分でそのためのメモリを確保し、そのメモリパケットのアドレスを exinf
に入れる。カーネルでは exinf
の内容について関知しない。
mbfatr
は、下位側がシステム属性を表し、上位側が実装独自属性を表す。mbfatr
のシステム属性の部分では、次のような指定を行う。
mbfatr:= (TA_TFIFO || TA_TPRI) | [TA_DSNAME] | [TA_NODISWAI]
TA_TFIFO | 送信待ちタスクのキューイングはFIFO |
TA_TPRI | 送信待ちタスクのキューイングは優先度順 |
TA_DSNAME | DSオブジェクト名称を指定する |
TA_NODISWAI | tk_dis_wai による待ち禁止を拒否する |
TA_TFIFO, TA_TPRI では、バッファが一杯の場合にメッセージを送信するタスクがメッセージバッファの待ち行列に並ぶ際の並び方を指定することができる。属性が TA_TFIFO であればタスクの待ち行列はFIFOとなり、属性が TA_TPRI であればタスクの待ち行列はタスクの優先度順となる。なお、メッセージキューの順序はFIFOのみである。
メッセージ受信待ちのタスクの待ち行列の順序はFIFOのみである。
TA_DSNAME を指定した場合に dsname
が有効となり、DSオブジェクト名称として設定される。DSオブジェクト名称はデバッガがオブジェクトを識別するために使用され、T-Kernel/DSのシステムコール td_ref_dsname と td_set_dsname からのみ操作可能である。詳細は td_ref_dsname 、td_set_dsname を参照のこと。TA_DSNAME を指定しなかった場合は、dsname
が無視され、td_ref_dsname や td_set_dsname が、E_OBJ エラーとなる。
#define TA_TFIFO 0x00000000 /* 送信待ちタスクをFIFOで管理 */ #define TA_TPRI 0x00000001 /* 送信待ちタスクを優先度順で管理 */ #define TA_DSNAME 0x00000040 /* DSオブジェクト名称を指定 */ #define TA_NODISWAI 0x00000080 /* 待ち禁止拒否 */
送信待ちのタスクが複数あった場合、バッファの空きができて送信待ちが解除されるのは常に待ち行列の順となる。
例えば、30バイトのメッセージを送信しようとしているタスクAと、10バイトのメッセージを送信しようとしているタスクBがA-Bの順で待っていた場合、メッセージバッファに20バイトの空きができてもAのタスクを追い越してBのタスクが先に送信することはない。
メッセージキューを入れるリングバッファの中には、一つ一つのメッセージを管理する情報も入るため、bufsz
で指定されたリングバッファのサイズとキューに入るメッセージのサイズの合計とは、一般には一致しない。後者の方が小さい値をとるのが普通である。その意味で、bufsz
の情報は厳密な意味をもつものではない。
bufsz
=0のメッセージバッファを生成することは可能である。この場合、このメッセージバッファでは送受信側が完全に同期した通信を行うことになる。すなわち、tk_snd_mbf と tk_rcv_mbf の一方のシステムコールが先に実行されると、それを実行したタスクは待ち状態となる。もう一方のシステムコールが実行された段階で、メッセージの受け渡し(コピー)が行われ、その後双方のタスクが実行を再開する。
bufsz
=0のメッセージバッファの場合、具体的な動作は次のようになる。
[図4]で、タスクAとタスクBは非同期に動いている。もし、タスクAが先に(1)に到達し、tk_snd_mbf(mbfid
) を実行した場合には、タスクBが(2)に到達するまでタスクAはメッセージ送信待ち状態になる。この状態のタスクAを対象として tk_ref_tsk を発行すると、tskwait
=TTW_SMBF となる。逆に、タスクBが先に(2)に到達し、tk_rcv_mbf(mbfid
) を実行した場合には、タスクAが(1)に到達するまでタスクBはメッセージ受信待ち状態になる。この状態のタスクBを対象として tk_ref_tsk を発行すると、tskwait
=TTW_RMBF となる。
タスクAが tk_snd_mbf(mbfid
) を実行し、かつタスクBが tk_rcv_mbf(mbfid
) を実行した時点でタスクAからタスクBにメッセージが送信され、どちらのタスクも待ち解除となって実行を再開する。
mbfid
で示されたメッセージバッファを削除する。
本システムコールの発行により、対象メッセージバッファのID番号および管理ブロック用の領域およびメッセージを入れるバッファ領域は解放される。
対象メッセージバッファにおいてメッセージ受信またはメッセージ送信を待っているタスクがあった場合にも、本システムコールは正常終了するが、待ち状態にあったタスクにはエラー E_DLT が返される。また、対象メッセージバッファの中にメッセージが残っている場合でも、エラーとはならず、メッセージバッファの削除が行われ、中にあったメッセージは消滅する。
E_OK | 正常終了 |
E_ID | 不正ID番号(mbfid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mbfid のメッセージバッファが存在しない) |
E_PAR | パラメータエラー(msgsz ≦0, msgsz >maxmsz , msg が不正, tmout ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象メッセージバッファが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_snd_mbf では、mbfid
で示されたメッセージバッファに対して、msg
のアドレスに入っているメッセージを送信する。メッセージのサイズは msgsz
で指定される。すなわち、msg
以下の msgsz
バイトが、mbfid
で指定されたメッセージバッファのメッセージキューにコピーされる。メッセージキューは、リングバッファによって実現されることを想定している。
msgsz
が、tk_cre_mbf で指定した maxmsz
よりも大きい場合は、エラー E_PAR となる。
バッファの空き領域が少なく、msg
のメッセージをメッセージキューに入れられない場合、本システムコールを発行したタスクはメッセージ送信待ち状態となり、バッファの空きを待つための待ち行列(送信待ち行列)につながれる。待ち行列の順序は tk_cre_mbf 時の指定によりFIFOまたはタスク優先度順となる。
tmout
により待ち時間の最大値(タイムアウト値)を指定することができる。タイムアウト指定が行われた場合、待ち解除の条件が満足されない(バッファに十分な空き領域ができない)まま tmout
の時間が経過すると、タイムアウトエラー E_TMOUT となってシステムコールが終了する。
tmout
としては、正の値のみを指定することができる。tmout
の基準時間(時間の単位)はシステム時刻の基準時間(=1ミリ秒)と同じである。
tmout
として TMO_POL=0を指定した場合は、タイムアウト値として0を指定したことを示し、バッファに十分な空きがない場合は待ちに入らず E_TMOUT を返す。また、tmout
として TMO_FEVR=(-1)を指定した場合は、タイムアウト値として無限大の時間を指定したことを示し、タイムアウトせずにバッファに空きができるまで待ち続ける。
長さが0のメッセージは送信することができない。msgsz
≦0の場合には、エラー E_PAR となる。
タスク独立部やディスパッチ禁止状態から実行した場合はエラー E_CTX となるが、tmout
=TMO_POL の場合は、実装によってはタスク独立部やディスパッチ禁止状態から実行することができる場合がある。
E_OK | 正常終了 |
E_ID | 不正ID番号(mbfid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mbfid のメッセージバッファが存在しない) |
E_PAR | パラメータエラー(msgsz ≦0, msgsz >maxmsz , msg が不正, tmout_u ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象メッセージバッファが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_snd_mbf のパラメータである tmout
を64ビットマイクロ秒単位の tmout_u
としたシステムコールである。
パラメータが tmout_u
となった点を除き、本システムコールの仕様は tk_snd_mbf と同じである。詳細は tk_snd_mbf の説明を参照のこと。
T-Kernel 2.0で追加されたシステムコールである。
E_ID | 不正ID番号(mbfid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mbfid のメッセージバッファが存在しない) |
E_PAR | パラメータエラー(msg が不正, tmout ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象メッセージバッファが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_rcv_mbf では、mbfid
で示されたメッセージバッファからメッセージを受信し、msg
で指定した領域に入れる。すなわち、mbfid
で指定されたメッセージバッファのメッセージキューの先頭のメッセージの内容を、msg
以下の msgsz
バイトにコピーする。
mbfid
で示されたメッセージバッファにまだメッセージが送信されていない場合(メッセージキューが空の場合) には、本システムコールを発行したタスクは待ち状態となり、メッセージの到着を待つ待ち行列(受信待ち行列)につながれる。受信待ちタスクの待ち行列はFIFOのみである。
tmout
により待ち時間の最大値(タイムアウト値)を指定することができる。タイムアウト指定が行われた場合、待ち解除の条件が満足されない(メッセージが到着しない)まま tmout
の時間が経過すると、タイムアウトエラー E_TMOUT となってシステムコールが終了する。
tmout
としては、正の値のみを指定することができる。tmout
の基準時間(時間の単位)はシステム時刻の基準時間(=1ミリ秒)と同じである。
tmout
として TMO_POL=0を指定した場合は、タイムアウト値として0を指定したことを示し、メッセージがない場合にも待ちに入らず E_TMOUT を返す。また、tmout
として TMO_FEVR=(-1)を指定した場合は、タイムアウト値として無限大の時間を指定したことを示し、タイムアウトせずにメッセージが到着するまで待ち続ける。
E_ID | 不正ID番号(mbfid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(mbfid のメッセージバッファが存在しない) |
E_PAR | パラメータエラー(msg が不正, tmout_u ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象メッセージバッファが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_rcv_mbf のパラメータである tmout
を64ビットマイクロ秒単位の tmout_u
としたシステムコールである。
パラメータが tmout_u
となった点を除き、本システムコールの仕様は tk_rcv_mbf と同じである。詳細は tk_rcv_mbf の説明を参照のこと。
T-Kernel 2.0で追加されたシステムコールである。
pk_rmbf
の内容
mbfid
で示された対象メッセージバッファの各種の状態を参照し、リターンパラメータとして送信待ちタスクのID(stsk
)、次に受信されるメッセージのサイズ(msgsz
)、空きバッファのサイズ(frbufsz
)、メッセージの最大長(maxmsz
)、受信待ちタスクのID(wtsk
)、拡張情報(exinf
) を返す。
wtsk
は、このメッセージバッファで受信待ちしているタスクのIDを示す。また、stsk
は送信待ちしているタスクのIDを示す。このメッセージバッファで複数のタスクが待っている場合には、待ち行列の先頭のタスクのIDを返す。待ちタスクが無い場合は0となる。
対象メッセージバッファが存在しない場合には、エラー E_NOEXS となる。
msgsz
には、メッセージキューの先頭のメッセージ(次に受信されるメッセージ)のサイズが返る。メッセージキューにメッセージが無い場合には、msgsz
=0となる。なお、サイズが0のメッセージを送ることはできない。
どんな場合でも、msgsz
=0とwtsk
=0の少なくとも一方は成り立つ。
frbufsz
は、メッセージキューを構成するリングバッファの空き領域のサイズを示すものである。この値は、あとどの程度の量のメッセージを送信できるかを知る手掛かりになる。
maxmsz
には、tk_cre_mbf で指定したメッセージの最大長が返される。
ランデブ機能は、複数のタスクがサーバとクライアントの関係にある場合に、それらのタスク間での同期通信を行う機能である。具体的には、クライアント側タスクとサーバ側タスクの双方が処理の受付を待ち合わせる機能、クライアント側タスクからサーバ側タスクに処理を依頼するメッセージ(呼出メッセージ)を送る機能、サーバ側タスクの処理の完了をクライアント側タスクが待つ機能、サーバ側タスクからクライアント側タスクに処理結果のメッセージ(返答メッセージ)を返す機能が含まれる。ランデブのシステムコールを使うことにより、上記のような複数の一連の処理を簡単な手順で実現できる。ランデブ機能は、ランデブポートと呼ばれるオブジェクトの上で動作する。
ランデブ機能には、ランデブポートを生成/削除する機能、ランデブポートに対して処理の依頼を行う機能(ランデブの呼出)、ランデブポートで処理依頼を受け付ける機能(ランデブの受付)、処理結果を返す機能(ランデブの終了)、受け付けた処理依頼を他のランデブポートに回送する機能(ランデブの回送)ランデブポートおよびランデブの状態を参照する機能が含まれる。ランデブポートはID番号で識別されるオブジェクトである。ランデブポートのID番号をランデブポートIDと呼ぶ。
ランデブポートに対して処理依頼を行う側のタスク(クライアント側のタスク)は、ランデブポートとランデブ条件、依頼する処理に関する情報を入れたメッセージ(これを呼出メッセージと呼ぶ)を指定して、ランデブの呼出を行う。一方、ランデブポートで処理依頼を受け付ける側のタスク(サーバ側のタスク)は、ランデブポートとランデブ条件を指定して、ランデブの受付を行う。
ランデブ条件は、ビットパターンで指定する。あるランデブポートに対して、呼び出したタスクのランデブ条件のビットパターンと、受け付けたタスクのランデブ条件のビットパターンをビット毎に論理積をとり、結果が0以外の場合にランデブが成立する。ランデブを呼び出したタスクは、ランデブが成立するまでランデブ呼出待ち状態となる。逆に、ランデブを受け付けるタスクは、ランデブが成立するまでランデブ受付待ち状態となる。
ランデブが成立すると、ランデブを呼び出したタスクから受け付けたタスクへ、呼出メッセージが渡される。ランデブを呼び出したタスクはランデブ終了待ち状態へ移行し、依頼した処理が完了するのを待つ。一方、ランデブを受け付けたタスクは待ち解除され、依頼された処理を行う。ランデブを受け付けたタスクが依頼された処理を完了すると、処理結果を返答メッセージの形で呼び出したタスクに渡し、ランデブを終了する。この時点で、ランデブを呼び出したタスクが、ランデブ終了待ち状態から待ち解除される。
上記の動作を[図6]の例を用いて説明する。この図で、タスクAとタスクBは非同期に実行しているものとする。
もしタスクAが先に tk_cal_por を呼び出した場合には、タスクBが tk_acp_por を呼び出すまでタスクAは待ち状態となる。この時タスクAは、ランデブの呼出待ち状態になっている[図6(a)]。
逆にタスクBが先に tk_acp_por を呼び出した場合には、タスクAが tk_cal_por を呼び出すまでタスクBは待ち状態となる。この時タスクBは、ランデブの受付待ち状態になっている[図6(b)]。
タスクAが tk_cal_por を呼び出し、タスクBが tk_acp_por を呼び出した時点でランデブが成立し、タスクAを待ち状態としたまま、タスクBが待ち解除される。この時タスクAは、ランデブの終了待ち状態になっている。
タスクBが tk_rpl_rdv を呼び出した時点で、タスクAは待ち解除される。その後は、両タスクとも実行できる状態となる。
ランデブポートは、ランデブ呼出待ち状態のタスクをつなぐための呼出待ち行列と、ランデブ受付待ち状態のタスクをつなぐための受付待ち行列を持つ。それに対して、ランデブが成立した後は、ランデブした双方のタスクはランデブポートから切り離される。すなわち、ランデブポートは、ランデブ終了待ち状態のタスクをつなぐための待ち行列は持たない。また、ランデブを受け付け、依頼された処理を実行しているタスクに関する情報も持たない。
カーネルは、同時に成立しているランデブを識別するために、ランデブ番号と呼ばれるユニークな番号を付与する。ランデブ番号の付与方法は実装依存であるが、少なくとも、ランデブを呼び出したタスクを指定するための情報を含んでいなければならない。また、同じタスクが呼び出したランデブであっても、1回目のランデブと2回目のランデブで異なるランデブ番号が付与される。
補足事項 | |
---|---|
ランデブ番号の付与方法の一例として、ランデブを呼び出したタスクのID番号をランデブ番号の下位ビットとし、その上位ビットにシーケンシャルな番号を付加する方法がある。 |
仕様決定の理由 | |
---|---|
本機能の名称である「ランデブ(rendezvous)」は、クライアント側タスクとサーバ側タスクの両者が「待ち合わせ」を行うことに由来している。また、本機能の導入に際しては、Ada言語のランデブや、その元になったCSP(Communicating Sequential Processes)の影響を受けている。ただし、T-Kernelが提供するランデブ機能とAda言語のランデブの機能が同一というわけではない。 ランデブ機能は、他の同期・通信機能を組み合わせて実現することも可能であるが、返答を伴う通信を行う場合には、それ専用の機能を用意した方が、アプリケーションプログラムが書きやすくなる。 また、他の同期・通信機能を組み合わせるよりも効率を上げることができると考えられる。一例として、ランデブ機能は、メッセージの受渡しが終わるまで双方のタスクを待たせておくために、メッセージを格納するための領域が必要ないという利点がある。 同じタスクが呼び出したランデブであっても、ランデブ番号をできる限りユニークにしなければならないのは、次の理由による。ランデブが成立してランデブ終了待ち状態となっているタスクが、タイムアウトや待ち状態の強制解除などにより待ち解除された後、再度ランデブを呼び出してランデブが成立した場合を考える。この時、最初のランデブのランデブ番号と、後のランデブのランデブ番号が同一の値であると、最初のランデブを終了させようとした時に、ランデブ番号が同一であるために後のランデブが終了してしまう。 2つのランデブに異なるランデブ番号を付与し、ランデブ終了待ち状態のタスクに待ち対象のランデブ番号を記憶しておけば、最初のランデブを終了させようとした時にエラーとすることができる。 |
pk_cpor
の内容
ランデブポートを生成しランデブポートID番号を割り当てる。具体的には、生成されたランデブポートに対して管理ブロックを割り付ける。ランデブポートは、ランデブを実現するためのプリミティブとなるオブジェクトである。
exinf
は、対象ランデブポートに関する情報を入れておくためにユーザが自由に利用できる。ここで設定した情報は、tk_ref_por で取り出すことができる。なお、ユーザの情報を入れるためにもっと大きな領域がほしい場合や、途中で内容を変更したい場合には、自分でそのためのメモリを確保し、そのメモリパケットのアドレスを exinf
に入れる。カーネルでは exinf
の内容について関知しない。
poratr
は、下位側がシステム属性を表し、上位側が実装独自属性を表す。poratr
のシステム属性の部分では、次のような指定を行う。
poratr:= (TA_TFIFO || TA_TPRI) | [TA_DSNAME] | [TA_NODISWAI]
TA_TFIFO | 呼出待ちタスクのキューイングはFIFO |
TA_TPRI | 呼出待ちタスクのキューイングは優先度順 |
TA_DSNAME | DSオブジェクト名称を指定する |
TA_NODISWAI | tk_dis_wai による待ち禁止を拒否する |
TA_TFIFO, TA_TPRI では、ランデブ呼出待ちのタスクの待ち行列の並び順を指定する。ランデブ受付待ちのタスクの待ち行列はFIFOのみである。
TA_DSNAME を指定した場合に dsname
が有効となり、DSオブジェクト名称として設定される。DSオブジェクト名称はデバッガがオブジェクトを識別するために使用され、T-Kernel/DSのシステムコール td_ref_dsname と td_set_dsname からのみ操作可能である。詳細は td_ref_dsname 、td_set_dsname を参照のこと。TA_DSNAME を指定しなかった場合は、dsname
が無視され、td_ref_dsname や td_set_dsname が、E_OBJ エラーとなる。
#define TA_TFIFO 0x00000000 /* 待ちタスクをFIFOで管理 */ #define TA_TPRI 0x00000001 /* 待ちタスクを優先度順で管理 */ #define TA_DSNAME 0x00000040 /* DSオブジェクト名称を指定 */ #define TA_NODISWAI 0x00000080 /* 待ち禁止拒否 */
maxcmsz
にはランデブの呼出時に渡すメッセージの最大サイズ(バイト数)を指定する。maxcmsz
に0を指定することも可能である。ただし、maxcmsz
に0を指定した場合は、ランデブの呼出時に渡すメッセージのサイズは0に限定され、メッセージ無しでの同期にのみ使用されることになる。
maxrmsz
にはランデブの返答時に渡すメッセージの最大サイズ(バイト数)を指定する。maxrmsz
に0を指定することも可能である。ただし、maxrmsz
に0を指定した場合はランデブの返答時に渡すメッセージのサイズは0に限定されることになる。
porid
で示されたランデブポートを削除する。
本システムコールの発行により、対象ランデブポートのID番号および管理ブロック用の領域は解放される。
対象ランデブポートにおいてランデブ受付(tk_acp_por)や呼出(tk_cal_por)を待っているタスクがあった場合にも、本システムコールは正常終了するが、待ち状態にあったタスクにはエラー E_DLT が返される。
tk_del_por によりランデブポートが削除されても、既にランデブ成立済のタスクに対しては、影響を与えない。ランデブ受付側タスク(待ち状態ではない) には何も通知されないし、ランデブ呼出側タスク(ランデブ終了待ち状態) の状態もそのままである。ランデブ受付側タスクが tk_rpl_rdv を行う時に、ランデブ成立に使ったランデブポートが既に削除されていても、tk_rpl_rdv は正常に実行される。
E_ID | 不正ID番号(porid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(porid のランデブポートが存在しない) |
E_PAR | パラメータエラー(cmsgsz <0, cmsgsz >maxcmsz , calptn =0, msg が不正, tmout ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ランデブポートが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
ランデブポートに対するランデブ呼出を行う。
tk_cal_por の具体的な動作は次のようになる。porid
で指定したランデブポートにおいてランデブ受付待ち状態のタスクがあり、そのタスクと tk_cal_por 発行タスクとの間でランデブ成立条件が満たされた場合は、ランデブ成立となる。この場合、ランデブ受付待ちだったタスクは実行可能状態(READY)となり、tk_cal_por 発行タスクはランデブ終了待ちの状態になる。ランデブ終了待ちの状態になったタスクは、ランデブの相手のタスク(ランデブ受付タスク)が tk_rpl_rdv を実行することにより待ち状態が解除される。この時点で tk_cal_por のシステムコールが終了する。
porid
で指定したランデブポートに受付待ちタスクが無かった場合や、受付待ちタスクがあってもランデブ成立条件が満たされなかった場合には、tk_cal_por 発行タスクはこのランデブポートの呼出側待ち行列に並びランデブ呼出待ちの状態となる。ランデブ呼出待ち行列の順序は、tk_cre_por の指定によりFIFOまたはタスク優先度順となる。
ランデブ成立条件は、受付側タスクの acpptn
と呼出側タスクの calptn
との論理積が0かどうかによって判定される。論理積が0でない場合にランデブ成立となる。calptn
が0の場合は、決してランデブが成立しなくなるので、パラメータエラー E_PAR とする。
ランデブ成立時には、呼出側タスクから受付側タスクに対してメッセージ(呼出メッセージ)を送ることができる。呼出メッセージのサイズは cmsgsz
で指定される。具体的には、呼出側タスクが tk_cal_por で指定した msg
以下の領域の cmsgsz
バイトが、受付側タスクが tk_acp_por で指定した msg
以下の領域にコピーされる。
逆に、ランデブ終了時には、受付側タスクから呼出側タスクに対してメッセージ(返答メッセージ)を送ることができる。具体的には、受付側タスクが tk_rpl_rdv で指定した返答メッセージの内容が、呼出側タスクが tk_cal_por で指定した msg
以下の領域にコピーされる。また、返答メッセージのサイズ rmsgsz
は、tk_cal_por のリターンパラメータとなる。結局、tk_cal_por の msg
パラメータで指定されたメッセージ領域は、tk_rpl_rdv 実行の際に送られてくるメッセージによって破壊されることになる。
なお、ランデブが回送された場合は、tk_cal_por で指定した msg
のアドレスから最大で maxrmsz
の領域をバッファとして使用するため、その内容を破壊する可能性がある。したがって、tk_cal_por で要求したランデブが回送される可能性がある場合は、期待する返答メッセージのサイズにかかわらず、msg
以下に少なくとも maxrmsz
のサイズの領域を確保しておかなければならない。(詳細は tk_fwd_por の項を参照)
cmsgsz
が、tk_cre_por で指定した maxcmsz
よりも大きい場合は、エラー E_PAR となる。このエラーはランデブ呼出待ち状態に入る前にチェックされ、エラーの場合、tk_cal_por を実行したタスクはランデブ呼出待ち状態には入らない。
tmout
によりランデブが成立するまでの待ち時間の最大値(タイムアウト値)を指定することができる。タイムアウト指定が行なわれた場合、待ち解除の条件が満足されない(ランデブが成立しない)まま tmout
の時間が経過すると、タイムアウトエラー E_TMOUT となってシステムコールが終了する。
tmout
としては、正の値のみを指定することができる。tmout
の基準時間(時間の単位)はシステム時刻の基準時間(=1ミリ秒)と同じである。
tmout
として TMO_POL=0を指定した場合は、タイムアウト値として0を指定したことを示し、対象ランデブポートにランデブ受付待ちタスクが無いか、ランデブ成立条件が満たされない場合には、待ちに入らず E_TMOUT を返す。
tmout
として TMO_FEVR=(-1)を指定した場合は、タイムアウト値として無限大の時間を指定したことを示し、タイムアウトすることなくランデブが成立するまで待ち続ける。
いずれにしても、tmout
の指定はランデブ成立までの時間に関するタイムアウトを意味するものであり、ランデブ成立からランデブ終了までの時間には関係しない。
E_ID | 不正ID番号(porid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(porid のランデブポートが存在しない) |
E_PAR | パラメータエラー(cmsgsz <0, cmsgsz >maxcmsz , calptn =0, msg が不正, tmout_u ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ランデブポートが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_cal_por のパラメータである tmout
を64ビットマイクロ秒単位の tmout_u
としたシステムコールである。
パラメータが tmout_u
となった点を除き、本システムコールの仕様は tk_cal_por と同じである。詳細は tk_cal_por の説明を参照のこと。
T-Kernel 2.0で追加されたシステムコールである。
E_ID | 不正ID番号(porid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(porid のランデブポートが存在しない) |
E_PAR | パラメータエラー(acpptn =0, msg が不正, tmout ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ランデブポートが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
ランデブポートに対するランデブ受付を行う。
tk_acp_por の具体的な動作は次のようになる。porid
で指定したランデブポートの呼出側待ち行列に入っているタスクと、このタスクとの間で、ランデブ成立条件が満たされた場合は、ランデブ成立となる。この場合、呼出側待ち行列にあったタスクは行列から外れ、ランデブ呼出待ち(ランデブ成立待ち)の状態からランデブ終了待ちの状態に変わる。tk_acp_por の発行タスクは、実行を継続する。
porid
で指定したランデブポートの呼出側待ち行列にタスクが無かった場合や、タスクがあってもランデブ成立条件が満たされなかった場合、tk_acp_por の発行タスクはそのランデブポートに対するランデブ受付待ち状態になる。この時、既に別のタスクがランデブ受付待ち状態であったとしても、エラーとはならず、tk_acp_por を発行したタスクはランデブ受付待ち行列につながれる。また、一つのランデブポートを使って、複数のタスクが同時にランデブを行うことが可能である。そのため、porid
で指定したランデブポートで別のタスクがランデブを行っている間に(前に成立したランデブに関する tk_rpl_rdv が実行される前に) に次のランデブを行っても、エラーとはならない。
ランデブ成立条件は、受付側タスクの acpptn
と呼出側タスクの calptn
との論理積が0かどうかによって判定される。論理積が0でない場合にランデブ成立となる。先頭のタスクが条件を満たさなければ、待ち行列の次のタスクについて順にチェックを行う。calptn
と acpptn
に0以外の同じ値を指定すれば、条件が無い(無条件)のと同じになる。また、acpptn
が0の場合は、決してランデブが成立しなくなるので、パラメータエラー E_PAR とする。ランデブ成立までの処理に関しては、ランデブ呼出側とランデブ受付側で完全に対称である。
ランデブ成立時には、呼出側タスクから受付側タスクに対して呼出メッセージを送ることができる。呼出側タスクが指定した呼出メッセージの内容は、受付側タスクが tk_acp_por で指定した msg
以下の領域にコピーされる。また、呼出メッセージのサイズ cmsgsz
は、tk_acp_por のリターンパラメータとなる。
ランデブ受付側のタスクが、同時に複数のランデブを行うことも可能である。具体的には、tk_acp_por によりあるランデブを受け付けたタスクが、tk_rpl_rdv を実行する前に、もう一度 tk_acp_por を実行しても構わない。また、この時の tk_acp_por は、前と異なるランデブポートを対象としたものであっても、前と同じランデブポートを対象としたものであっても構わない。特殊な例であるが、ランデブ中のタスクが同じランデブポートに対してもう一度 tk_acp_por を実行してランデブが成立した場合、同一のタスクが同一のランデブポートに対して複数の(多重の)ランデブを行っているという状態になる。もちろん、この場合にランデブの相手(呼出側タスク)は異なっている。
tk_acp_por のリターンパラメータとして返される rdvno
は、同時に成立している複数のランデブを区別するための情報であり、ランデブ終了時に tk_rpl_rdv のパラメータとして使用する。また、ランデブ回送時には tk_fwd_por のパラメータとして使用する。rdvno
の具体的な内容は実装依存であるが、ランデブ成立相手の呼出側タスクを指定するための情報を含んでいるはずである。
tmout
により待ち時間の最大値(タイムアウト値)を指定することができる。タイムアウト指定が行われた場合、待ち解除の条件が満足されない(ランデブが成立しない)まま tmout
の時間が経過すると、タイムアウトエラー E_TMOUT となってシステムコールが終了する。
tmout
としては、正の値のみを指定することができる。tmout
の基準時間(時間の単位)はシステム時刻の基準時間(=1ミリ秒)と同じである。
tmout
として TMO_POL=0を指定した場合は、タイムアウト値として0を指定したことを示し、対象ランデブポートにランデブ呼出待ちタスクが無いか、ランデブ成立条件が満たされない場合には、待ちに入らず E_TMOUT を返す。また、tmout
として TMO_FEVR=(-1)を指定した場合は、タイムアウト値として無限大の時間を指定したことを示し、タイムアウトすることなくランデブの成立まで待ち続ける。
ランデブ受付側のタスクも待ち行列を作る機能は、同じ処理をするサーバを複数個並列に走らせるような時に有用である。また、ランデブポートをタスク独立にしたという特徴を活かすことができる。
ランデブを受け付けたタスクが、何らかの理由でランデブ終了前(tk_rpl_rdv 実行前) に異常終了したような場合は、tk_cal_por を実行したランデブ呼出側のタスクがランデブ終了待ち状態から解放されないまま残ることになる。このようなケースを避けるためには、ランデブ受付側タスクの異常終了時に tk_rpl_rdv または tk_rel_wai を実行し、ランデブがエラーで終了したことをランデブ呼出側のタスクにも通知しておく必要がある。
rdvno
にはランデブ成立相手の呼出側タスクを指定する情報を含むが、その番号の割当て方法は、できるだけユニークになるように配慮しなければならない。同一タスク間のランデブであっても、1回目のランデブと2回目のランデブでは異なる rdvno
を割り当てる必要がある。この配慮により、以下のような問題を回避できる。
tk_cal_por を実行してランデブ終了待ちのタスクが、tk_rel_wai や tk_ter_tsk+tk_sta_tsk 等によって強制的に待ち解除となり、再度 tk_cal_por を実行してランデブが成立した場合を考える。最初のランデブに対する rdvno
と後のランデブに対する rdvno
が同じ値であると、最初のランデブに対する tk_rpl_rdv によって後のランデブが終了してしまう。rdvno
の割当てをユニークにし、ランデブ終了待ちタスクの側で期待する rdvno
を覚えておけば、最初のランデブに対する tk_rpl_rdv のエラーを検出することができる。
rdvno
の具体的な実現方法としては、たとえば、rdvno
の下位バイトを呼出側タスクのID、上位バイトをシーケンシャルな番号とすればよい。
calptn
, acpptn
によるランデブ成立条件の機能を使えば、ランデブの選択受付(Adaのselectに相当)の機能が実現可能となる。Adaのselect文の例[図7]に相当する具体的な処理方法を[図8]に示す。
図 7. select文を使ったAdaのプログラム例
select when condition_A accept entry_A do ... end; or when condition_B accept entry_B do ... end; or when condition_C accept entry_C do ... end; end select;
図 8. ランデブによるAdaのselect機能の実現方法
entry_A, entry_B, entry_C がそれぞれ一つのランデブポートに対応するのではなく、select文全体が一つのランデブポートに対応する。
entry_A, entry_B, entry_C を、calptn
, acpptn
の 2^0, 2^1, 2^2 のビットに対応させる。
Adaのプログラム例の中のselect文は、次のようになる。
ptn := 0; if conditon_A then ptn := ptn + 2^0 endif; if conditon_B then ptn := ptn + 2^1 endif; if conditon_C then ptn := ptn + 2^2 endif; tk_acp_por(acpptn := ptn);
もし、プログラム例中のselect文以外に、select無しの単なるentry_Aのacceptがあれば、
tk_acp_por(acpptn := 2^0);を実行すれば良い。また、entry_A, entry_B, entry_Cを無条件にORで待ちたい時は、
tk_acp_por(acpptn := 2^2+2^1+2^0);を実行すれば良い。
一方、これを呼び出す側は、entry_Aの呼出であれば
tk_cal_por(calptn := 2^0);を実行し、entry_Cの呼出であれば
tk_cal_por(calptn := 2^2);を実行すれば良い。
Adaの選択機能は受け付け側にしか用意されていないが、ランデブでは、calptn
で複数のビットを指定することにより、呼出側に選択機能を持たせることも可能である。
ランデブ成立条件に関して呼出側と受付側が全く対称であるにもかかわらず、tk_cal_por と tk_acp_por が別システムコールとなっているのは、ランデブ成立後の処理が異なるからである。すなわち、呼出側はランデブ成立後に待ち状態になるのに対して、受付側はランデブ成立後に実行可能状態(READY)となる。
E_ID | 不正ID番号(porid が不正あるいは利用できない) |
E_NOEXS | オブジェクトが存在していない(porid のランデブポートが存在しない) |
E_PAR | パラメータエラー(acpptn =0, msg が不正, tmout_u ≦(-2)) |
E_DLT | 待ちオブジェクトが削除された(待ちの間に対象ランデブポートが削除) |
E_RLWAI | 待ち状態強制解除(待ちの間に tk_rel_wai を受け付け) |
E_DISWAI | 待ち禁止による待ち解除 |
E_TMOUT | ポーリング失敗またはタイムアウト |
E_CTX | コンテキストエラー(タスク独立部またはディスパッチ禁止状態で実行) |
tk_acp_por のパラメータである tmout
を64ビットマイクロ秒単位の tmout_u
としたシステムコールである。
パラメータが tmout_u
となった点を除き、本システムコールの仕様は tk_acp_por と同じである。詳細は tk_acp_por の説明を参照のこと。
T-Kernel 2.0で追加されたシステムコールである。
一旦受け付けたランデブを別のランデブポートに回送する。
このシステムコールを発行したタスク(タスクXとする)は、現在ランデブ中の状態(tk_acp_por を実行した後の状態)でなければならない。また、ランデブ相手の呼出側タスクはタスクY、tk_acp_por のリターンパラメータとして返されたランデブ番号は rdvno
であるものとする。その状態で tk_fwd_por を実行すると、タスクXとタスクYとの間のランデブ状態が解除され、その後 porid
で示される別のランデブポート(ランデブポートB)に対してタスクYがランデブ呼出を行ったのと同じ状況になる。
tk_fwd_por の具体的な動作は次のようになる。
rdvno
で示されるランデブを解除する。
タスクYを、porid
のランデブポートに対してランデブ呼出待ちの状態にする。この時、ランデブ成立のための呼出側選択条件を表すビットパターン calptn
は、タスクYが tk_cal_por で指定したものではなく、タスクXが tk_fwd_por で指定したものが使用される。タスクYから見ると、ランデブ終了待ちの状態からランデブ呼出待ちの状態に戻ることになる。
その後、porid
のランデブポートに対するランデブが受け付けられれば、それを受け付けたタスクとタスクYとの間でランデブ成立となる。もちろん、porid
のランデブポートに既にランデブ受付待ちタスクが存在し、ランデブ成立条件が満たされていれば、tk_fwd_por の実行により即座にランデブ成立となることもある。ここで、ランデブ成立時に受付側に送られるメッセージは、タスクYが tk_cal_por で指定したものではなく、タスクXが tk_fwd_por で指定したものが使用される(calptn
と同様)。
新しいランデブが終了した際に tk_rpl_rdv で返されるメッセージは、タスクXが tk_fwd_por で指定した msg
以下の領域ではなく、タスクYが tk_cal_por で指定した msg
以下の領域にコピーされる。
基本的には、
「tk_cal_por (porid
=portA, calptn
=ptnA, msg
=mesA) の後で tk_fwd_por (porid
=portB, calptn
=ptnB, msg
=mesB) が実行された状態」
と、
「tk_cal_por (porid
=portB, calptn
=ptnB, msg
=mesB) が実行された状態」
とは全く同じ状態になる。結果的に、カーネルとしてはランデブ回送の履歴を覚えておく必要はないことになる。
tk_fwd_por によってランデブ呼出待ち状態に戻ったタスクに対して tk_ref_tsk を実行した場合、tskwait
は TTW_CAL となる。また、wid
も回送先のランデブポートのID番号になる。
tk_fwd_por の実行は即座に終了する。このシステムコールで待ち状態になることはない。また、tk_fwd_por の実行が終わった後の tk_fwd_por 発行タスクは、回送前のランデブが成立したランデブポート、回送後のランデブポート(porid
のランデブポート)、それらの上でランデブを行ったタスクのいずれとも無関係になる。
cmsgsz
が、回送後のランデブポートの maxcmsz
よりも大きい場合は、エラー E_PAR となる。このエラーはランデブの回送を行う前にチェックされる。エラーの場合、ランデブの回送は行われず、rdvno
で示されるランデブも解除されない。
tk_fwd_por で指定した送信メッセージは、tk_fwd_por 実行時に他の領域(たとえば tk_cal_por で指定したメッセージ領域)にコピーされる。したがって、回送されたランデブが成立する前に tk_fwd_por の msg
で示されるメッセージ領域の内容が変更されても、回送されたランデブはその影響を受けない。
tk_fwd_por でランデブを回送する場合、回送後のランデブポート(porid
のランデブポート)の maxrmsz
は、回送前のランデブの成立したランデブポートの maxrmsz
と等しいか、それよりも小さくなければならない。回送後のランデブポートの maxrmsz
が回送前のランデブポートの maxrmsz
よりも大きかった場合は、回送先のランデブポートが不適当であるという意味で、E_OBJ のエラーとなる。ランデブ呼出側では、回送前のランデブポートの maxrmsz
に合わせて返答メッセージ受信領域を用意しているため、ランデブの回送によって返答メッセージの最大サイズが大きくなると、呼出側に対して予期せぬ大きさの返答メッセージを返す可能性を生じ、問題を起こす。maxrmsz
の大きなランデブポートにランデブを回送できないのは、このような理由による。
また、tk_fwd_por で送信するメッセージのサイズ cmsgsz
についても、回送前のランデブの成立したランデブポートの maxrmsz
と等しいか、それよりも小さくなければならない。これは、tk_fwd_por の実装方法として、tk_cal_por で指定したメッセージ領域を送信メッセージのバッファとして使うことを想定しているためである。cmsgsz
が回送前のランデブポートの maxrmsz
より大きかった場合には、E_PAR のエラーとなる。(詳細は【補足事項】を参照)
タスク独立部から tk_fwd_por, tk_rpl_rdv を発行する必要はないが、ディスパッチ禁止中あるいは割込み禁止中のタスクから tk_fwd_por, tk_rpl_rdv を発行することは可能である。この機能は、tk_fwd_por や tk_rpl_rdv と不可分に何らかの処理を行う場合に利用できる。なお、タスク独立部から tk_fwd_por, tk_rpl_rdv が発行された場合のエラーチェックは実装依存である。
tk_fwd_por により、ランデブ終了待ち状態であったタスクYがランデブ呼出待ちの状態に戻った場合、次にランデブが成立するまでのタイムアウトは、常に永久待ち(TMO_FEVR) として扱われる。
回送先のランデブポートは、前のランデブに使っていたランデブポート(rdvno
のランデブが成立したランデブポート)と同じランデブポートであっても構わない。この場合は、tk_fwd_por によって、一旦受け付けたランデブの受付処理をとりやめることになる。ただし、この場合でも、呼出メッセージや calptn
は、呼出側タスクが tk_cal_por で指定したものではなく、受付側タスクが tk_fwd_por で指定したものに変更される。
一旦回送されてきたランデブを、さらに回送することも可能である。
tk_fwd_por を使ったサーバタスクの動作イメージを[図9][2] に示す。
一般に、tk_fwd_por を実行するのは、[図9]に示されるようなサーバ分配タスク(サーバの受け付けた処理を別のタスクに分配するためのタスク)である。したがって、tk_fwd_por を実行したサーバ分配タスクは、回送したランデブの成立の可否にかかわらず、次の要求を受け付ける処理に移らなければならない。その場合、tk_fwd_por のメッセージ領域は次の要求を処理するために利用されるので、メッセージ領域の内容を変更しても、前に処理したランデブの回送には影響しないようにしなければならない。このため、tk_fwd_por の実行後は、回送されたランデブの成立前であっても、tk_fwd_por の msg
で示されるメッセージ領域の内容を変更することが可能でなければならない。
実装上は、この仕様を実現するために、tk_cal_por で指定したメッセージ領域をバッファとして使うことが許される。すなわち、tk_fwd_por の処理では、tk_fwd_por で指定した呼出メッセージを tk_cal_por の msg
で指定したメッセージ領域にコピーし、tk_fwd_por 発行タスクがメッセージ領域の内容を変更しても構わないようにする。ランデブ成立時には、ランデブが回送されたものかどうかにかかわらず、tk_cal_por のメッセージ領域に置かれていたメッセージが受付側に渡される。
このような実装方法を適用できるように、以下のような仕様を設けている。
tk_cal_por で要求したランデブが回送される可能性がある場合は、期待する返答メッセージのサイズにかかわらず、tk_cal_por の msg
以下に少なくとも maxrmsz
のサイズの領域を確保しておかなければならない。
tk_fwd_por で送信するメッセージのサイズ cmsgsz
は、回送前のランデブポートの maxrmsz
と等しいか、それよりも小さくなければならない。
tk_fwd_por によりランデブが回送される場合に、回送後のランデブポートの maxrmsz
が回送前のランデブポートの maxrmsz
より大きくなることはない。等しい値をとるか、小さくなっていく。
システム全体で持つべき状態の数を減らすため、tk_fwd_por の仕様は、ランデブ回送の履歴を保存しないという前提で設計されている。ランデブ回送の履歴を覚える必要がある用途では、tk_fwd_por ではなく、tk_cal_por~tk_acp_por の組をネストして使えばよい。
ランデブの相手のタスク(呼出側タスク)に対して返答を返し、ランデブを終了する。
このシステムコールを発行したタスク(タスクXとする)は、ランデブ中の状態(tk_acp_por を実行した後の状態)でなければならない。また、ランデブ相手の呼出側タスクはタスクY、tk_acp_por のリターンパラメータとして返されたランデブ番号は rdvno
であるものとする。その状態で tk_rpl_rdv を実行すると、タスクXとタスクYとの間のランデブ状態が解除され、ランデブ終了待ち状態にあった呼出側タスクYは実行可能状態(READY)に戻る。
tk_rpl_rdv でランデブを終了する時には、受付側タスクXから呼出側タスクYに対して返答メッセージを送ることができる。受付側タスクが指定した返答メッセージの内容は、呼出側タスクが tk_cal_por で指定した msg
以下の領域にコピーされる。また、返答メッセージのサイズ rmsgsz
は、tk_cal_por のリターンパラメータとなる。
rmsgsz
が、tk_cre_por で指定した maxrmsz
よりも大きい場合は、エラー E_PAR となる。このエラーが検出された場合、ランデブは終了せず、tk_cal_por を実行したタスクのランデブ終了待ち状態は解除されない。
タスク独立部から tk_fwd_por, tk_rpl_rdv を発行することはできないが、ディスパッチ禁止中あるいは割込み禁止中のタスクから tk_fwd_por, tk_rpl_rdv を発行することは可能である。この機能は、tk_fwd_por や tk_rpl_rdv と不可分に何らかの処理を行う場合に利用できる。なお、タスク独立部から tk_fwd_por, tk_rpl_rdv が発行された場合のエラーチェックは実装依存である。
ランデブを呼び出したタスクが、何らかの理由でランデブ終了前(tk_rpl_rdv 実行前) に異常終了したような場合にも、ランデブ受付側のタスクは直接それを知ることができない。この場合は、ランデブ受付側のタスクが tk_rpl_rdv を実行する時に E_OBJ のエラーとなる。
ランデブ成立後は、原則としてタスクとランデブポートとが切り離されてしまう(相互に情報を参照する必要がない) が、tk_rpl_rdv のメッセージ長のチェックに使用する maxrmsz
のみは、ランデブポートに依存した情報であるため、ランデブ中のタスクがどこかに覚えておく必要がある。実装上の工夫としては、待ち状態になっている呼出側タスクのTCB、あるいはTCBから参照可能な領域(スタック等)に入れておくことが考えられる。
tk_rpl_rdv や tk_fwd_por のパラメータでは、成立中のランデブを区別する情報として rdvno
を指定するが、ランデブ成立に利用したランデブポートのID(porid
) は指定しない。これは、ランデブ成立後のタスクがランデブポートとは無関係になるという方針に基づいたものである。
rdvno
が不正値の場合のエラーコードとして、E_PAR ではなく E_OBJ を使用している。これは、rdvno
の実体が呼出側タスクを示しているという理由による。
pk_rpor
の内容
porid
で示された対象ランデブポートの各種の状態を参照し、リターンパラメータとして受付待ちタスクのID(atsk
)、呼出待ちタスクのID(wtsk
)、メッセージの最大長(maxcmsz
,maxrmsz
)、拡張情報(exinf
) を返す。
wtsk
は、このランデブポートでランデブ呼出待ちになっているタスクのIDを示す。ランデブ呼出待ちタスクが無い場合は wtsk
=0となる。一方、atsk
は、このランデブポートでランデブ受付待ちになっているタスクのIDを示す。ランデブ受付待ちタスクが無い場合は atsk
=0となる。
このランデブポートで複数のタスクが呼出待ち状態または受付待ち状態になっている場合には、それぞれ呼出待ち行列または受付待ち行列の先頭のタスクのIDを返す。
対象ランデブポートが存在しない場合には、エラー E_NOEXS となる。
このシステムコールでは、現在ランデブ中のタスクに関する情報を知ることはできない。
[1] | ベース優先度:ミューテックスによって自動的に引き上げられる前のタスクの優先度を示す。最後(ミューテックスのロック中も含む)に tk_chg_pri によって設定された優先度、または tk_chg_pri を一度も発行していない場合はタスク生成時に指定したタスク優先度が、ベース優先度である。 |
[2] |
|