遠隔手続き呼び出しのときにデータは XDR 標準形式で伝送されますので、XDR は Sun の RPC パッケージの重要な基本概念となっています。複数の異なるタイプのマシンからアクセスされる (読み書きされる) データの転送には、XDR ライブラリルーチンを使用する必要があります。
XDR は異なる言語、異なるオペレーティングシステム、異なるマシンアーキテクチャの間で機能します。ほとんどのユーザー (特に RPC ユーザー) は、「整数フィルタ」、「浮動小数点フィルタ」、「列挙型フィルタ」の節を読むだけで十分でしょう。RPC と XDR を別のマシン上に実装したいプログラマは、このテクニカルノートやプロトコルの仕様に関心を持たれるかも知れません。
RPC 呼び出しを行わない場合でも、rpcgen を使用して XDR ルーチンを書くことができます。
XDR ルーチンを使用する C プログラムはファイル <rpc/xdr.h> をインクルードしなければなりません。<rpc/xdr.h> には、XDR システムとの必要なインタフェースがすべて入っています。XDR ルーチンはすべてライブラリ libnsl.a に入っていますので、コンパイルは次のコマンドで実行します。
example% cc program.c |
移植性を保つためには、各環境でさまざまな基準を守ってプログラミングしなければなりません。プログラミング方法の微妙な違いを見つけるのも必ずしも簡単とはいえませんが、それらが広範囲に渡って関連し合っています。以下に示すサンプルプログラム 例 A-1 と 例 A-2 (テキスト行の読み込みと書き出しのプログラム) で考えてみましょう。
#include <stdio.h> main() /* writer.c */ { int i; for (i = 0; i < 8; i++) { if (fwrite((char *) &i, sizeof(i), 1, stdout) != 1) { fprintf(stderr, "failed!¥n"); exit(1); } } exit(0); } |
#include <stdio.h> main() /* reader.c */ { int i, j; for (j = 0; j < 8; j++) { if (fread((char *) &i, sizeof(i), 1, stdin) != 1) { fprintf(stderr, "failed!¥n"); exit(1); } printf("%ld ", i); } printf("¥n"); exit(0); } |
この 2 つのプログラムは移植可能に見えます。なぜならば、(a) lint チェックをパスし、(b) 任意のハードウェアアーキテクチャ上で、ローカルに実行しても同じように動作するからです。
writer プログラムの出力を reader プログラムにパイプした場合も、SPARC と Intel の両方で同じ結果が得られます。
sun% writer | reader 0 1 2 3 4 5 6 7 sun% intel% writer | reader 0 1 2 3 4 5 6 7 intel% |
ローカルエリアネットワークと 4.2BSD の出現に伴って、「ネットワークパイプ」の概念が導入されました。すなわち、あるマシン上のプロセスで生成したデータを、別のマシン上のプロセスが使用するという概念です。writer と reader のプログラムでも、ネットワークパイプを使用できます。最初に SPARC 上でデータを生成し、そのデータを Intel 上で使用したときの実行結果を次に示します。
sun% writer | rsh intel reader 0 16777216 33554432 50331648 67108864 83886080 100663296 117440512 sun% |
writer を Intel で実行し、reader を SPARC で実行した場合も同じ結果になります。この原因は、Intel と SPARC の int 型整数は、ワードサイズは同じでもデータのバイト順序が異なるためです。
16777216 は 224 です。4 バイトを逆順にすると、24 番目のビットに 1 が置かれます。
複数の異なるタイプのマシンでデータを共有するときは、データの可搬性についての注意が必要です。read() と write() 呼び出しを XDR ライブラリルーチンの呼び出しで xdr_int() に置き換えることにより、データが可搬なプログラムになります。xdr_int() は、int 型の整数の外部用の標準的な表現を扱うことができるフィルタです。writerプログラムの改良版を例 A-3に示します。
#include <stdio.h> #include <rpc/rpc.h> /* XDR は RPC のサブライブラリ */ main() /* writer.c */ { XDR xdrs; int i; xdrstdio_create(&xdrs, stdout, XDR_ENCODE); for (i = 0; i < 8; i++) { if (!xdr_int(&xdrs, &i)) { fprintf(stderr, "failed!¥n"); exit(1); } } exit(0); } |
例 A-4 のサンプルプログラムは、reader プログラムを修正したものです。
#include <stdio.h> #include <rpc/rpc.h> /* xdr は rpc のサブライブラリ */ main() /* reader.c */ { XDR xdrs; int i, j; xdrstdio_create(&xdrs, stdin, XDR_DECODE); for (j = 0; j < 8; j++) { if (!xdr_int(&xdrs, &i)) { fprintf(stderr, "failed!¥n"); exit(1); } printf("%ld ", i); } printf("¥n"); exit(0); } |
変更したプログラムを SPARC 上で実行した結果、Intel 上で実行した結果、および SPARC からIntel にパイプした結果を次に示します。
sun% writer | reader 0 1 2 3 4 5 6 7 sun% intel% writer | reader 0 1 2 3 4 5 6 7 intel% sun% writer | rsh intel reader 0 1 2 3 4 5 6 7 sun% |
整数データの移植問題は、データの移植問題のごく簡単な一例にすぎません。どのデータ構造体にも移植性の問題があり、特にデータ境界とポインタが大きな問題になります。ワード境界の違いにより、マシンごとに構造体のサイズが異なる可能性があります。ポインタは使用するには便利ですが、ポインタが定義されたマシンの外部では何の意味も持ちません。