+----------------------------+ | Ninf Executable Protocol | +----------------------------+ | Ninf Server Protocol | +----------------------------+ | TCP/IP | +----------------------------+
このレベルのプロトコルは、MAX_PKT_LEN
以下のパケットを単位とし
て通信する。パケットのheaderには、以下の情報が格納されている。
+---------------------+ | Packet Size | 4 bytes +---------------------+ | Packet Code | 4 bytes +---------------------+ | Argument 1 | 4 bytes +---------------------+ | Argument 2 | 4 bytes +---------------------+ | | = = Packet Size - 16 bytes | | +---------------------+
Ninf Serverには、登録されているNinf Executable と関数名の対応表があり、関 数名を指定して、このテーブルのindexを取得して、このindexを用いて、 Ninf Executable を起動する。
パケットのコマンドは以下の通りである。
NINF_PKT_REQ_STUB_INFO: 4
NINF_PKT_RPY_STUB_INFO
パケットで返される。
その関数の index(サーバ内での管理番号)が arg1 として
関数のインタフェースがデータ部に与えられる。
+----+-+-+-+---------------------------+ |size|4|X|X| xdr encoded function name | +----+-+-+-+---------------------------+
NINF_PKT_REQ_CALL: 6
NINF_PKT_RPY_CALL
が返される。
+----+-+-----+-+ | 16 |6|index|X| +----+-+-----+-+
NINF_PKT_KILL: 1
+----+-+-+-+ | 16 |1|X|X| +----+-+-+-+
NINF_PKT_TO_STUB: 2
+----+-+-+-+---------------------------+ |size|2|X|X| executable protocol data | +----+-+-+-+---------------------------+
NINF_PKT_RPY_STUB_INFO: 5
NINF_PKT_REQ_STUB_INFO
に対する応答パケット。arg1にindexが格納さ
れており、データ部で インタフェース を返す。
+----+-+-----+-+-----------+ |size|5|index|X| interface | +----+-+-----+-+-----------+
NINF_PKT_RPY_CALL: 7
NINF_PKT_REQ_CALL
が成功したことを通知する。
+----+-+-+-+ | 16 |7|X|X| +----+-+-+-+
NINF_PKT_TO_CLIENT: 3
+----+-+-+-+---------------------------+ |size|3|X|X| executable protocol data | +----+-+-+-+---------------------------+
NINF_PKT_ERROR: -1
+----+-+-+-+ | 16 |7|X|X| +----+-+-+-+
図4に、remote client Ninf_call
の概要を示す。
NINF_PKT_TO_STUB
パケットに埋め込まれる。
Ninf Server はこのパケットのヘッダを取り外し、中身を
Ninf Executable へのストリームに流す。
Ninf Executable からクライアントプログラムへの通信は、
NINF_PKT_TO_CLIENT
に埋め込まれる。
Ninf Server が Ninf Executable からのストリームを切りわけ、
ヘッダをつけてNINF_PKT_TO_CLIENT
のパケットとして
クライアントプログラムへ送信する。
NinfExecutable プロトコルは最初に下記のコマンドのコードをXDRで integerとしてエンコードしたものを送り、 必要に応じて引数をその後に続けて送るものである。
Client -> Executable
NINF_REQ_STUB_INFO: 2
成功時のNinfExcutable の返答は、 まずNINF_OK コードが返され、 そのあとにインタフェースをXDRでエンコードした ものが送られる。
通常、クライアントはサーバから インタフェース情報を取得するので クライアントからこのコードを用いることはない。
NINF_REQ_CALL: 1
成功時には、まずNINF_OK コードが返され、 その後に出力データが転送されてくる。
NINF_REQ_KILL: 3
NINF_ACK_OK: 0
NINF_ACK_ERROR: -1
/* get stub interface */ /* make packet and send */ init_data(DATA); add_data(DATA, NinfLibraryName); PKT = make_packet(NINF_PKT_REQ_STUB_INFO, dummy, DATA); send_pkt(PKT); /* receive pacekt and decode */ PKT = receive_pkt(); if (NINF_PKT_RPY_STUB_INFO == get_code(PKT)) interface = decode_stub_interface(get_data(PKT)); index = get_arg1(PKT); /* index represent the NinfLibrary */ PKT = make_packet(NINF_PKT_REQ_CALL, index, dummy_data); send_pkt(PKT); PKT = receive_pkt(); if (get_code(PKT) != NINF_PKT_RPY_CALL){ /* failed */ } /* request call accepted */ /* setup data for the NinfExecutable */ init_data(DATA); add_data(DATA, NINF_REQ_CALL); add_data(DATA, encodeded_parameters); /* make packet and send */ PKT = make_packet(NINF_PKT_TO_STUB, dummy, encoded_parametaers); send_pkt(PKT); /* receive packet */ PKT = receive_pkt(); if (get_code(PKT) != NINF_PKT_TO_CLIENT){ /* failed */ } DATA = get_data(PKT); if (get_first_data(DATA) != NINF_OK){ /* failed */ } decode_parameter(get_rest_data(DATA));ここでは、Ninf_call の入力パラメータ出力パラメータともに 1パケットに収まっているかのように書いたが、もちろん 複数のパケット分割しなければならない場合のほうがおおい。 その場合はDATAを単に分割して、それぞれのパケットに 納めて送れば良い。
typedef struct ninf_stub_information { int version_major,version_minor; /* protocol version */ int info_type; /* information type */ char module_name[MAX_NAME_LEN]; /* module name */ char entry_name[MAX_NAME_LEN]; /* entry name */ int nparam; struct ninf_param_desc * params; int order_type; NINF_EXPRESSION order; char * description; };version_major, version_minor はNinf のプロトコルのバージョン 番号であるが実際には使われていない。 info_type も同様である。 module_name/entry_nameはそれぞれライブラリのモジュール名/ルーチン名 である。 nparam は ライブラリのパラメータの数をあらわす。 params は パラメータの構造を格納した構造体 ninf_param_desc の 配列へのポインタになっている。 order_type, order は計算時間を推測するための計算のオーダーを 格納するため変数であるが、現在は用いられていない。
構造体 ninf_param_desc を下に示す。
struct ninf_param_desc { enum data_type param_type; /* argument type */ enum mode_spec param_inout; /* IN/OUT */ int ndim; /* number of dimensions */ struct { VALUE_TYPE size_type; int size; NINF_EXPRESSION size_exp; VALUE_TYPE start_type; int start; NINF_EXPRESSION start_exp; VALUE_TYPE end_type; int end; NINF_EXPRESSION end_exp; VALUE_TYPE step_type; int step; NINF_EXPRESSION step_exp; } dim[MAX_DIM]; };param_type はパラメータのデータ型を示す。 下のenumeration を用いる。
typedef enum data_type { UNDEF, /* undefined */ VOID, CHAR, SHORT, INT, LONG, LONGLONG, UNSIGNED_CHAR, UNSIGNED_SHORT, UNSIGNED, UNSIGNED_LONG, UNSIGNED_LONGLONG, FLOAT, DOUBLE, LONG_DOUBLE, STRING_TYPE, FUNC_TYPE, BASIC_TYPE_END } DATA_TYPE;param_inout は パラメータのI/Oモードを示す。 以下のenum で表現する。
typedef enum mode_spec { MODE_NONE = 0, MODE_IN = 1, /* default */ MODE_OUT = 2, MODE_INOUT = 3, MODE_WORK = 4, /* mode for work space */ } MODE_SPEC;ndim にはパラメータの次元数を与える。 構造体dim はパラメータの各次元の大きさ/ストライド等の情報を 保持する。 ここで注意しなければならないのは次元の低い順に 納められることである。 例えば3次元の配列であれば、 1次元目(最低次元)の情報が dim[0] に、 3次元目の情報がdim[2]に納められる。 各次元の情報は、size, start, end, step で構成されている。 これらはそれぞれ、その次元のサイズ、転送を要する項目の始点/終点/ ステップを示している。 size, start, end, step の各情報は
VALUE_TYPE XXX_type; int XXX; NINF_EXPRESSION XXX_exp;という三つ組みで表されている。これらは type は以下のenumerate で与えられる。
typedef enum value_type { VALUE_NONE = 0, /* default */ VALUE_CONST = 1, /* default, given in constant */ VALUE_IN_ARG = 2, /* specified by IN scalar paramter */ VALUE_BY_EXPR = 3, /* computed by interpreter */ VALUE_OP = 4, /* operation code */ VALUE_END_OF_OP = 5, /* end of expression */ VALUE_ERROR = -1 } VALUE_TYPE;XXX_type が VALUE_CONSTの時には、 XXX の値がこの三つ組みの値として 与えられる。 VALUE_IN_ARG のときには XXX番目のパラメータ(スカラーでなければならない)の値が 値となる。 VALUE_BY_EXPR の時には XXX_exp を評価した値が用いられる。 XXX_exp は下のような構造体で表される。 これは type と val のペアが NINF_EXPRESSION_LENGTH(20)個並んだ構造 である。式を逆ポーランド記法で記述する。
typedef struct ninf_expression { VALUE_TYPE type[NINF_EXPRESSION_LENGTH]; int val[NINF_EXPRESSION_LENGTH]; } NINF_EXPRESSION;type と val のペアの意味は上述の XXX_type と XXX のペアと同じ意味であるが、 ここでは、 XXX_type に VALUE_OP を使うことができる。 VALUE_OPは 演算子を意味する。演算子は以下のように定義されている。
#define OP_VALUE_PLUS 1 #define OP_VALUE_MINUS 2 #define OP_VALUE_MUL 3 #define OP_VALUE_DIV 4 #define OP_VALUE_MOD 5 #define OP_VALUE_UN_MINUS 6 #define OP_VALUE_BEKI 7
Module sample; Define mmul(long mode_in int n, mode_in double A[n][n+1-1], mode_in double B[n][n+2-3+1], mode_out double C[n*n]) Required "sample.o" CalcOrder n^3 Calls "C" mmul(n,A,B,C);というIDLで定義されたインタフェースがどのように表現されるか 見てみよう。
{ 0,0,0, "sample","mmul",4, param_desc, 3, { {2,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,3,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, stub_description };param_desc は下のように定義されている。
struct ninf_param_desc param_desc[] = { { 5, 1, 0,}, { 13, 1, 2,{ { 3, 2, { {2,1,4,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,1,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, { 2, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, }}, { 13, 1, 2,{ { 3, 1, { {2,1,4,1,4,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,2,1,3,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, { 2, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, }}, { 13, 2, 1,{ { 3, 3, { {2,2,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, }}, };一つずつ見ていこう。
第一パラメータは
{ 5, 1, 0,},と表されている。これは、このパラメータが long(param_type = 5) であり、mode は in (param_inout = 1) であり スカラ値(ndim = 0)であることを意味している。
第二パラメータは
{ 13, 1, 2,{ { 3, 2, { {2,1,4,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,1,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, { 2, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},}, }},と表されている。これは、このパラメータが double(param_type = 13) であり、mode は in (param_inout = 1) であり 2次元の配列(ndim = 2)であることを意味している。
// size { 3, 2, { {2,1,4,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,1,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, // start 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, // end 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}}, // step 0, 0, { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}},},が一つの次元の記述である。
3
はこの値が 式であることを意味する。
この式の元の形は、 n + 1 - 1
であるがこれを
逆ポーランド記法で書くと n 1 + 1 -
となる。
{2,1,4,1,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,} {0,1,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}がこの式に対応する。 [2,0] が 0番目の引数 n を表す。 [1,1] は 定数 1を 、[4,1]は'+' を [4,2] は '-' を示す。[5, 0] は式のターミネータである。
trans_int(version_major); trans_int(version_minor); trans_int(info_type); trans_string(module_name); trans_string(entry_name); trans_int(nparam); for (int i = 0; i < nparam; i++){ trans_enum(params[i]->param_type); trans_enum(params[i]->param_inout); trans_int (params[i]->ndim); for (int j = 0; j < params[i]->ndim; i++){ trans_enum(((params[i]->dim)[j]).size_type); trans_int (((params[i]->dim)[j]).size); if (((params[i]->dim)[j]).size_type == VALUE_BY_EXPR) for (int k = 0; k < NINF_EXPRESSION_LENGTH; k++){ trans_int(((params[i]->dim)[j]).size_exp.type[k]); trans_int(((params[i]->dim)[j]).size_exp.val[k]); } trans_enum(((params[i]->dim)[j]).start_type); trans_int (((params[i]->dim)[j]).start); if (((params[i]->dim)[j]).start_type == VALUE_BY_EXPR) for (int k = 0; k < NINF_EXPRESSION_LENGTH; k++){ trans_int(((params[i]->dim)[j]).start_exp.type[k]); trans_int(((params[i]->dim)[j]).start_exp.val[k]); } trans_enum(((params[i]->dim)[j]).end_type); trans_int (((params[i]->dim)[j]).end); if (((params[i]->dim)[j]).end_type == VALUE_BY_EXPR) for (int k = 0; k < NINF_EXPRESSION_LENGTH; k++){ trans_int(((params[i]->dim)[j]).end_exp.type[k]); trans_int(((params[i]->dim)[j]).end_exp.val[k]); } trans_enum(((params[i]->dim)[j]).step_type); trans_int (((params[i]->dim)[j]).step); if (((params[i]->dim)[j]).step_type == VALUE_BY_EXPR) for (int k = 0; k < NINF_EXPRESSION_LENGTH; k++){ trans_int(((params[i]->dim)[j]).step_exp.type[k]); trans_int(((params[i]->dim)[j]).step_exp.val[k]); } } } trans_enum(order_type); if (order_type == VALUE_BY_EXPR) for(int k = 0; k < NINF_EXPRESSION_LENGTH; k++){ trans_int(order.type[k]); trans_int(order.val[k]); } trans_string(description);
dim が -1 の時には読み出し側では転送サイズを動的に決定する。 すなわち、まず32ビットのinteger を 2つ読み出し、 それの積をサイズと考えてreadする。
配列の要素にstart,end,step が指定されている場合には、 指定されている部分だけを同様にXDRでエンコードして転送する。
ライブラリからの出力データも同様にXDRでエンコードされてくる。 Ninfでは出力データはすべて配列値なので、スカラを特別に 考える必要はない。