%% $OpenXM: OpenXM/doc/OpenXM-specs/stackmachine.tex,v 1.1.1.1 2000/01/20 08:52:46 noro Exp $ //&jp \section{ OX スタックマシン } //&eg \section{ OX stackmachine } (This section has not yet been translated.) /*&jp この節では, OX スタックマシン operator の説明 (TCP/IP ソケット上での標準 encoding 法 を用いる), および, サンプルサーバとリンクする場合または open XM ライブラリとしてリンクして使用する場合の ための C の関数の仕様を説明する. 説明の前に, OX サーバスタックマシンの動作の原則を 説明しておく. サーバスタックマシンは, {\tt SM\_pop*} 系のスタックマシンコマンドがこないかぎり, 自発的にメッセージを送信することはない. この原則に基づいて分散計算のプログラミングをおこなう. イベントドリブンなプログラム法とはちがうことに 注意しよう. \subsection{サーバスタックマシン } サンプルサーバである {\tt oxserver00.c} は以下の仕様の C の関数を用意して, {\tt nullstackmachine.c } を置き換えれば一応動作するはずである. \noindent \subsubsection{サーバスタックマシンのグループ SMobject/Basic0 に属するオペレータ} \noindent サーバスタックマシンは最低で1本のスタック \begin{verbatim} Object xxx_OperandStack[SIZE]; \end{verbatim} をもつ. ここで, {\tt Object} はそのシステム固有の Object 型で構わない. CMObject は各サーバ固有のローカルオブジェクトに変換して スタックへプッシュしてよい. ただし変換, 逆変換を合成したものは恒等写像であることがのぞましい. CMObject をどのように (local) Object に変換するか, Object が受け付けるメッセージの定義は, 各システムが独自にきめて文書化しておくものとする. つまりすべてのメッセージは, private である. たとえば, {\tt add } のような基本的な メッセージにたいしても, OX スタックマシンはなにもきめていない. 将来的には open math \cite{openmath} のように CMObject に対する最大公約数的なメッセージの仕様を content dictionary (CD) の形で定義したい. 以下, \verb+ xxx_ + は誤解の恐れがないときは省略する. \verb+ xxx_ + は local サーバシステムに固有の識別子である. {\tt Asir} の場合は \verb+ Asir_ + を用いる. {\tt kan/sm1} の場合は \verb+ Sm1_ + を用いる. 関数名, タグ名は長いので省略形を用いてもよい. 以下では次のようにパケットを記述する. 各フィールドは, \fbox{データ型 \quad データ} なる形式で書く. たとえば, {\tt int32 OX\_DATA} は 32 bit network byte order の数字 {\tt OX\_DATA} という意味である. ``イタリックで書かれているフィールドは, 定義が別のところでなされているか解釈に誤解のないような自然言語 で説明されている object を表す.'' たとえば, {\it String commandName} は, String データ型の local object {\it commandName} を意味する. (サーバスタックマシン上の object は, CMO 形式の object とは限らないことに注意. CMO 形式で書いてあっても, それはサーバスタックマシンの local 形式でスタック上にあると解釈して下さい.) すべてのサーバスタックマシンは 以下の関数を実装していないといけない. \begin{enumerate} \item CMObject/Basic0 の CMO データのうち必須のもの, {\tt CMO\_ERROR2}, {\tt CMO\_NULL}, {\tt CMO\_INT32}, {\tt CMO\_STRING}, {\tt CMO\_LIST} がおくられて来た場合 それをスタックに push する. たとえば, {\tt CMO\_NULL} の場合次のようになる. \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_NULL} \\ \hline \end{tabular} \\ Stack after the request: \begin{tabular}{|c|} \hline {\it NULL} \\ \hline \end{tabular} \\ Result: なし. たとえば, {\tt CMO\_String} の場合次のようになる. \\ Request: \begin{tabular}{|c|c|c|c|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_String} &{\tt int32} {\rm size} &{\tt byte} {\rm s1} & $\cdots$ &{\tt byte} {\rm ssize}\\ \hline \end{tabular} \\ Stack after the request: \begin{tabular}{|c|} \hline {\it String s} \\ \hline \end{tabular} \\ Result: なし. CMO データの受け取りに失敗した時のみ \\ \begin{tabular}{|c|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_ERROR2} & {\it CMObject} ob\\ \hline \end{tabular} \\ をスタックへ push する. 現在のところ, ob には, \\ \centerline{ [{\sl Integer32} OX パケット番号, {\sl Integer32} エラー番号, {\sl CMObject} optional 情報] } なるリストを入れる (CMO 形式でかいてあるが, これはサーバ独自の形式でよい. CMO として送出されるときこのような形式でないといけないという意味である.) \item \begin{verbatim} void *xxx_mathCap() \end{verbatim} このサーバの mathcap をもどす (termcap のまね). サーバのタイプ, サーバスタックマシンの能力を知ることができる. C 言語で実装する場合は, mathCap の構造体をシステム毎にきめるものとし, この関数はその構造体へのポインタを戻す. (open sm1 では {\tt struct mathCap} を用いている. @plugin/mathcap.h) \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_mathcap} \\ \hline \end{tabular} \\ Result: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_DATA} & {\sl Mathcap} mathCapOb \\ \hline \end{tabular} \item \begin{verbatim} xxx_setMathCap(void *m) \end{verbatim} 受け取った Mathcap {\tt m} を 自分のシステムに設定して, 相手側が理解不能な CMO をおくらないように する. C 言語で実装する場合は, mathCap の構造体をシステム毎にきめるものとし, この関数はその構造体へのポインタを引数とする. (open sm1 では {\tt struct mathCap} を用いている. @plugin/mathcap.h) \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_DATA} & {\sl Mathcap} m \\ \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_setMathCap} \\ \hline \end{tabular} \\ Result: なし. \\ 注意: mathcap は一般にクライアント主体で設定する. クライアントがサーバに {\tt SM\_mathcap} をおくり, サーバ側の mathcap を得る. それを, クライアントはそのサーバに付随した mathcap として 設定する. 次に, クライアントはサーバに自分の mathcap を {\tt SM\_setMathCap} でおくり, 自分の mathcap を設定させる. \item \begin{verbatim} int xxx_executeStringByLocalParser(char *s) \end{verbatim} 文字列 $s$ をシステム xxx の文法(サーバスタックマシンの組み込みローカル 言語)にしたがったコマンドとして実行する. ただし, コマンドの実行の結果の最後に戻り値があるときは, {\tt OperandStack} に戻り値を push する. 正常終了なら 0 を, 異常終了なら -1 をもどす. debug モードにはいった場合, -2 を戻す. エラーの時 Error2 Object を stack へ push する.\\ {\tt kan/sm1} の場合, サーバスタックマシンの組み込みローカル言語は {\tt sm1} ポストスクリプト言語である. サーバスタックマシンと, {\tt sm1} ポストスクリプト言語はスタックを 共有するように実装されている. 実際の計算は {\tt executeStringByLocalParser} により実行される. open XM では, 現在のところ関数名の標準化はおこなっていない. したがって, 実際の計算コマンドの送出は mathcap をみてクライアントが 正しいコマンドを選択する必要がある. (しかしながら, 共通関数名がないために, 共通仕様のサーバスタックマシンの 実装はきわめて簡単である. 関数名の共通化は将来の課題.) \\ 割込みに関しては, -1 を戻すように ハンドラを書く. executeStringByLocalParser() を再帰的に呼んだときも 割り込みのハンドラが 正しく動作するようにこの関数は書かれるべきである. この関数を呼び出したのち, signal, setjmp の再設定を呼び出し側でやらないと いけない. \\ この関数および {\tt popString} の機能を実現すれば, 最低限の open XM のサーバになれる. 実装では, まずこの二つの関数の機能を 実現すべきである. \\ Stack before the request: \\ \begin{tabular}{|c|} \hline {\it String commandString} \\ \hline \end{tabular} \\Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND}& {\tt int32 SM\_executeStringByLocalParser} \\ \hline \end{tabular} \\ Result: なし. \\ 参考: \ 実行前のスタックのデータは, {\it String commandString} なる local stackmachine の object として スタック上にあるが, TCP/IP の通信路では, 次のようなデータがまずながれて {\it commandName} がスタックに push される: \\ \begin{tabular}{|c|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_string} & {\it size and the string commandString} \\ \hline \end{tabular} \item \begin{verbatim} int xxx_executeStringByLocalParserInBatchMode(char *s) \end{verbatim} スタックに副作用がない(スタックにたいしてなんの操作もしない) ことを除き上とまったく同じ関数である. エラーの時のみ, Error2 Object をスタックへプッシュする. \item \begin{verbatim} char *xxx_popString(void) void xxx_popString(ox_stream out) \end{verbatim} 最初の関数はライブラリとしてリンクして使用する場合の関数である. {\tt OperandStack} より Object を pop し, それを xxx の出力 規則にしたがい文字列型に変換して戻す. スタックが空のときは, {\tt (char *)NULL} を戻す. 呼出側の関数は, 戻り値のメモリー領域を操作してはいけない. また, 再度 サーバスタックマシンが呼ばれたときは, 戻り値のメモリ領域 は変更されているかもしれない. 2 番目の関数は, TCP/IP を用いて通信する場合の関数である. 変換されてでてきた文字列を値として返すのではなく, {\tt ox\_stream out} へ CMO のデータとして送信する. エラーの場合は {\tt CMO\_ERROR2} を戻すべきである. \\ Stack before the request: \begin{tabular}{|c|} \hline {\it Object} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_popString} \\ \hline \end{tabular} \\ Result: \begin{tabular}{|c|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_STRING} & {\it size and the string s} \\ \hline \end{tabular} \item \begin{verbatim} int xxx_getsp(void) \end{verbatim} 現在のスタックポインタの位置をもどす. スタート時点での位置は 0 であり, object が push されたばあい, 1 づつ増えるものとする. \\ Stack before the request: \begin{tabular}{|c|} \hline {\it Object} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_getsp} \\ \hline \end{tabular} \\ Result: \begin{tabular}{|c|c|c|} \hline {\tt int32 OX\_DATA} & {\tt int32 CMO\_INT32} & {\it stack pointer value} \\ \hline \end{tabular} \item \begin{verbatim} object xxx_dupErrors(void) \end{verbatim} スタック上のエラーオブジェクトをリストにして戻す. スタック自体は変化させない. \\ Stack before the request: \begin{tabular}{|c|} \hline {\it Object} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_dupErrors} \\ \hline \end{tabular} \\ Result: \begin{tabular}{|c|c|c|} \hline {\tt int32 OX\_DATA} & {\sl CMObject} \ a list of errors\\ \hline \end{tabular} \item \begin{verbatim} int xxx_start() \end{verbatim} xxx の初期化をおこなう. \end{enumerate} \noindent {\bf 例}: \ 次は, 標準入力よりの入力を asir に送り評価した結果を表示するプログラム である. \begin{verbatim} #include main() { char tmp[1024]; Asir_start(); while (gets(tmp) != NULL) { if (Asir_executeStringByLocalParser(tmp) != 0) { printf("%s\n",Asir_popString()); } } } \end{verbatim} \medbreak \noindent {\bf 例}: \ mathcap の問い合わせに対して, {\tt ox\_sm1} は次のように答える. %%Prog: [(cmoMathCap)] extension :: \begin{verbatim} Class.mathcap [ [199909080 , $Ox_system=ox_sm1.plain$ , $Version=2.990911$ , $HOSTTYPE=i386$ ] , [262 , 263 , 264 , 265 , 266 , 268 , 269 , 272 , 273 , 275 , 276 ] , [[514] , [2130706434 , 1 , 2 , 4 , 5 , 17 , 19 , 20 , 22 , 23 , 24 , 25 , 26 , 30 , 31 , 60 , 61 , 27 , 33 , 40 , 34 ]]] \end{verbatim} mathcap は 3つの要素をもつリストである. まづ, 最初の要素を見よう. Ox\_system は open xxx システム名である. 読み込むライブラリがちがっていて, 関数名(または シンボル)の意味がちがうときは この名前もかえる. たとえば, open math の basic content dictionary 対応の関数定義マクロを 読みこんだ sm1 は, ox\_sm1\_basicCD なる名前にする. HOSTTYPE値は, CPU の種類をあらわし unix では環境変数\verb+$HOSTTYPE+ の値である. 2 番目の要素は 利用可能な SM コマンドをあつめたリストである. 3 番目のリストは, 処理可能な数学データの形式, およびCMOの場合なら 処理可能なCMOのタグのリストが続く. 上の例では, 514 は {\tt OX\_DATA} をあらわし, 数学データのフォマットは (サイズ情報なしの) CMO であることを示す. \medbreak \noindent {\bf 例}: \ %%Prog: (ox.sm1) run sm1connectr [(oxWatch) ox.ccc] extension %%Prog: ox.ccc (122345; ) oxsubmit ; {\tt message\_body} の実例をあげる. シリアル番号部は除いてある. \begin{enumerate} \item {\tt executeStringByLocalParser("12345 ;");} は次のようなパケットに変換される. 各数字は 16進1バイトをあらわす. {\tt xx(yy)} のなかの {\tt (yy)} は対応するアスキーコードをあわらす. \begin{verbatim} 0 0 2 2 0 0 0 4 0 0 0 7 31(1) 32(2) 33(3) 34(4) 35(5) 20 3b(;) 0 0 2 1 0 0 1 c \end{verbatim} ここで, \verb+ 0 0 2 2 0 0 0 4 + は, network byte order で, 順番に {\tt OX\_DATA} それから, CMO のタグの, {\tt CMO\_STRING} を表す. \verb+ 0 0 0 7 + は文字数, 最後に 文字列 {\tt 12345 ;} が来る. ここで, \verb+ 0 0 1 c + は, network byte order で, {\tt OX\_executeString} を表す. まとめると次のようになる. \begin{verbatim} 0 0 2 2 (OX_DATA) 0 0 0 4 (CMO_STRING) 0 0 0 7 (size) 31(1) 32(2) 33(3) 34(4) 35(5) 20 3b(;) (data) 0 0 2 1 (OX_COMMAND) 0 0 1 c (SM_executeStringByLocalParser) \end{verbatim} これを OXexpression で表記すると次のようになる. \begin{center} (OX\_DATA, (CMO\_STRING, 7, "12345 ;")) \end{center} \begin{center} (OX\_COMMAND, (SM\_executeStringByLocalParser)) \end{center} \item {\tt popString()} を要請するメッセージ: \begin{verbatim} 0 0 2 1 (OX_COMMAND) 0 0 1 7 (SM_popString) \end{verbatim} OXexpression では (OX\_COMMAND, (SM\_popString)). \noindent これにたいして次の返答メッセージがくる. \begin{verbatim} 0 0 2 2 (OX_DATA) 0 0 0 4 (CMO_STRING) 0 0 0 5 (size) 31(1) 32(2) 33(3) 34(4) 35(5) \end{verbatim} OXexpression でかくと, (OX\_DATA, (CMO\_STRING, 7, "12345 ;")). \end{enumerate} \subsubsection{グループ SMobject/Basic1 に属するオペレータ} \begin{enumerate} \item \begin{verbatim} void xxx_pops(int n) \end{verbatim} operand stack より, {\it n} 個の元 ({\it obj1, obj2, $\ldots$, objn}) を pop して捨てる. \\ Stack before the request: (右が stack のトップである.) \\ \begin{tabular}{|c|c|c|c|c|} \hline {\it obj1} & {\it obj2} & $\cdots$ & {\it objn} &{\it INT32 n} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_pops } \\ \hline \end{tabular} \\ Result: なし. \item \begin{verbatim} int xxx_setName(char *name) \end{verbatim} (これは本当に必要な関数??) {\tt OperandStack} より {\it name} を pop し, つぎに {\tt OperandStack} より {\it obj} を pop し, それを 現在の名前空間で変数 {\it name} に bind する. 正常終了なら 0 を, 異常終了なら -1 をもどす. TCP/IP による通信では, 異常終了の時のみ, {\tt CMO\_ERROR2} を stack へ push する. \\ Stack before the request: (右が stack の top.) \begin{tabular}{|c|c|} \hline {\it obj} & {\it String name} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_setName} \\ \hline \end{tabular} \\ Result: なし. \item \begin{verbatim} int xxx_evalName(char *name) \end{verbatim} (これは本当に必要な関数??) 現在の名前空間で変数 {\it name} を評価する. 評価の結果 {\it resultObj} をスタックへ戻す. 関数自体は正常終了なら 0 を, 異常終了なら -1 をもどす. TCP/IP の場合, 異常終了の場合のみ {\tt CMO\_ERROR2} を stack へ push する. \\ Stack before the request: (右が stack の top.) \begin{tabular}{|c|} \hline {\it String name} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_evalName} \\ \hline \end{tabular} \\ Stack after the request: (右が stack の top.) \begin{tabular}{|c|} \hline {\it resultObj} \\ \hline \end{tabular} \\ Result: なし. \item \begin{verbatim} int xxx_executeFunction(char *s, int n) \end{verbatim} スタックより {\it n} 個のデータを pop して, サーバのローカル関数 {\it s} を実行する. エラーのときのみ {\tt CMO\_ERROR2} を stack へ push する. \\ Stack before the request: (右が stack の top.) \\ \begin{tabular}{|c|c|c|c|c|} \hline {\it objn} & $\cdots$ & {\it obj1} & {\it INT32 n} & {\it String s} \\ \hline \end{tabular} \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 SM\_executeFunction} \\ \hline \end{tabular} \\ Stack after the request: 関数実行の結果. \\ Result: なし. \item \begin{verbatim} bytes *xxx_popSerializedLocalObject(void) void popSerializedLocalObject(ox_stream out) \end{verbatim} 最初の関数はライブラリとしてリンクして使用するための関数である. スタックより pop した object を local 形式の serialization して byte 列で戻す. 2 番目の関数は Socket で通信するための同機能の関数であり, serialization された byte 列を {\tt ox\_stream out} へ出力する. この場合, header {\tt int32 OX\_DATA}, {\tt int32 CMO\_LOCAL\_OBJECT} をつけてから, byte 列を送る. この関数はおもに, homogeneous な分散システムで用いる. 次に, サーバスタックマシンの関数が実行されるまでは, 戻り object の内容は保証されないといけない. \item local serialized object, および サポートする CMO object を {\tt OX\_DATA} として受信した場合, {\tt OperandStack} へ push する. 受信エラーを起こしたときのみ, {\tt CMO\_ERROR2} を stack へ push する. \item \begin{verbatim} bytes *xxx_popCMO(void) void xxx_popCMO(ox_stream out) \end{verbatim} 最初の関数はライブラリとしてリンクして使用するための関数である. {\tt OperandStack} より object を pop し CMO 形式の serialized object を byte 列として戻す. 2 番目の関数は Socket で通信するための同機能の関数であり, {\tt ox\_stream out } へ, そのデータを header {\tt OX\_DATA} をつけてながす. この関数はおもに, heterotic な分散システムで用いる. 次に, サーバスタックマシンの関数が実行されるまでは, 戻り object の内容は保証されないといけない. \\ Request: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_COMMAND} & {\tt int32 OX\_popCMO} \\ \hline \end{tabular} \\ Result: \begin{tabular}{|c|c|} \hline {\tt int32 OX\_DATA} & {\it Serialized CMO} \\ \hline \end{tabular} 以下で, {\tt ox\_stream} はサンプルサーバの場合, \begin{verbatim} typedef FILE2 * ox_stream; \end{verbatim} である (cf. {\tt file2.h}). これは処理系によりちがってよい. {\tt ox\_asir} では, {\tt FILE *} を用いている. \end{enumerate} */