ONC+ 開発ガイド

コンパイル時に指定するフラグ

この節では、コンパイル時に使用可能な rpcgen オプションについて説明します。次の表に、この節で説明するオプションを要約します。

表 3-2 rpcgen コンパイル時に指定するフラグ

オプション 

フラグ 

コメント 

テンプレート

-a, -Sc, -Ss, -Sm

表 3-3 を参照

C 形式

-N

新しい形式のモードを呼び出す 

ANSI C

-C

-N オプションとともに使用

マルチスレッド対応コード

-M

マルチスレッド環境で使用 

マルチスレッド自動モード

-A

このオプションを指定すると、-M も自動的に指定される

TS-RPC ライブラリ

-b

デフォルトは TI-RPC ライブラリ 

xdr_inline カウント

-i

デフォルトはパックされた 5 つの要素。他の数字も指定できる

クライアント側とサーバ側のテンプレート

rpcgen で次のフラグを指定して、クライアント側とサーバ側のテンプレートを生成することができます。

表 3-3 rpcgen テンプレート選択フラグ

フラグ 

機能 

-a

すべてのテンプレートを生成 

-Sc

クライアント側のテンプレートを生成 

-Ss

サーバ側のテンプレートを生成 

-Sm

makefile のテンプレートを生成

生成されたテンプレートファイルを参考にしてプログラムを書くか、テンプレートに抜けている部分を直接書き込んで使用します。rpcgen は、スタブプログラムのほかにこれらのテンプレートファイルを生成します。

ソースプログラム add.x から C 形式モードでサーバ側テンプレートを生成するときは、次のコマンドを実行します。

rpcgen -N -Ss -o add_server_template.c add.x

生成されたテンプレートファイルは add_server_template.c という名前になります。同じソースプログラム add.x から C 形式モードでクライアント側テンプレートを生成するときは、次のコマンド行を実行します。

rpcgen -N -Sc -o add_client_template.c add.x

生成されたテンプレートファイルは add_client_template.c という名前になります。同じソースプログラム add.x から makefile テンプレートを生成するときは、次のコマンド行を実行します。

rpcgen -N -Sm -o mkfile_template add.x

生成されたテンプレートファイルは mkfile_template という名前になります。このファイルを使用して、サーバ側とクライアント側のプログラムをコンパイルできます。次のように、-a フラグを指定した場合は、

rpcgen -N -a add.x

3 つのテンプレートファイルがすべて生成されます。クライアント側テンプレートは add_client.c、サーバ側テンプレートは add_server.cmakefile テンプレートは makefile.a という名前になります。このうち1 つでも同名のファイルが存在していれば、rpcgen はエラーメッセージを表示して終了します。


注 -

テンプレートファイルを生成する際には、次に rpcgen が実行された時に上書きされないように新しい名前を付けてください。


C 形式モード

-N フラグを指定して rpcgen を起動すると、C 形式モード (Newstyle モードとも呼ばれる)で処理が行われます。このモードでは、引数は値で渡され、複数の引数も構造体にしないで渡すことができます。この機能を使用すると、RPC コードを、C 言語やその他の高級言語に近い形式で書くことができます。既存のプログラムや makefile との互換性を保つため、従来モード (標準モード) の方がデフォルトになっています。次の例では、-N フラグにより利用できる機能を示します。従来モードと C 形式モードの両方のソースモジュールを、例 3-8例 3-9に示します。


例 3-8 C 形式モードの add.x

/*
 * このプログラムには、2 つの数値を加える手続きが入っています。
 * ここでは、C 形式モードによる引数の引き渡し方法を示します。
 * 関数 add() が 2 つの引数を取ることに注意してください。
 */
program ADDPROG {					/* プログラム番号 */
	version ADDVER {					/* バージョン番号 */
		int add(int, int) = 1;		/* 手続き */
	} = 1;
} = 0x20000199;


例 3-9 デフォルトモードの add.x

/*
 * このプログラムには、2 つの数値を加える手続きが入っています。
 * ここでは、デフォルトモードによる引数の引き渡し方法を示します。
 * デフォルトモードの場合、rpcgen は引数を 1 つしか処理しないことに
 * 注意してください。
 */
struct add_arg {
	int first;
	int second;
};
program ADDPROG {					/* プログラム番号 */
	version ADDVER {					/* バージョン番号 */
		int add (add_arg) = 1;		/* 手続き */
	} = 1;
} = 0x20000199;

例 3-10 から例 3-13 には、生成されるクライアント側テンプレートを示します。


例 3-10 C 形式モードのクライアント側スタブプログラムの例 : add.x

/*
 * C 形式のクライアント側メインルーチン。
 * 遠隔 RPC サーバ上の関数 add() を呼び出します。
 */
#include <stdio.h>
#include "add.h"

main(argc, argv)
int argc;
char *argv[];
{
	CLIENT *clnt;
	int *result,x,y;
	
	if(argc != 4) {
		printf("usage: %s host num1 
					num2¥n" argv[0]);
		exit(1);
	}
	/* 
 * クライアントハンドルの作成 - サーバに結合
 */
	clnt = clnt_create(argv[1], ADDPROG,
								ADDVER, "udp");
	if (clnt == NULL) {
		clnt_pcreateerror(argv[1]);
		exit(1);
	}
	x = atoi(argv[2]);
	y = atoi(argv[3]);
/* 遠隔手続きの呼び出し: add_1() には、ポインタではなく、
 * 複数の引数が渡されていることに注意してください。
 */
	result = add_1(x, y, clnt);
	if (result == (int *) NULL) {
		clnt_perror(clnt, "call failed:");
		exit(1);
	} else {
		printf("Success: %d + %d = %d¥n", 
					x, y, *result);
	}
	exit(0);
}

例 3-11 に、デフォルトモードと C 形式モードとのコードの相違点を示します。


例 3-11 デフォルトモードのクライアント側スタブプログラムの例

	arg.first = atoi(argv[2]);
	arg.second = atoi(argv[3]);
/*
 * 遠隔手続きの呼び出し -- クライアント側スタブプログラムには、
 * 引数へのポインタを渡さなければならないことに注意してください。
 */
	result = add_1(&arg, clnt);

例 3-12 に、C 形式モードのサーバ側手続きを示します。


例 3-12 C 形式モードのサーバ側プログラムの例

#include "add.h"

int *
add_1(arg1, arg2, rqstp)
	int arg1;
	int arg2;
	struct svc_req *rqstp;
{
	static int result;

	result = arg1 + arg2;
	return(&result);
}

例 3-13 に、デフォルトモードのサーバ側手続きを示します。


例 3-13 デフォルトモードのサーバ側スタブプログラムの例

#include "add.h"
int *
add_1(argp, rqstp)
	add_arg *argp;
	struct svc_req *rqstp;
{
	static int result;

	result = argp->first + argp->second;
	return(&result);
}

マルチスレッド対応のコード

デフォルトでは、rpcgen で生成されるコードはマルチスレッド対応になりません。グローバル変数は保護されず、戻り値も静的変数で返されます。マルチスレッド環境で実行できるマルチスレッド対応コードを生成するには、-M フラグを指定します。-M フラグは、-N-C のどちらか (または両方) のフラグと共に指定します。

この機能を使用したマルチスレッド対応プログラムの例を示します。例 3-14rpcgen のプロトコルファイル msg.x を示します。


例 3-14 マルチスレッド対応プログラム : msg.x

program MESSAGEPROG {
version PRINTMESSAGE {
        int PRINTMESSAGE(string) = 1;
        } = 1;
} = 0x4001;

文字列が遠隔手続きに渡され、遠隔手続きでは文字列を表示してから文字数をクライアントに返します。マルチスレッド対応のテンプレートを生成するには、次のコマンドを実行します。

% rpcgen -M msg.x

例 3-15 に、クライアント側のコードを示します。


例 3-15 マルチスレッド対応のクライアント側スタブプログラム

#include "msg.h"

void
messageprog_1(host)
	char *host;
{
	CLIENT *clnt;
	enum clnt_stat retval_1;
	int result_1;
	char * printmessage_1_arg;

	clnt = clnt_create(host, MESSAGEPROG, 
									PRINTMESSAGE,
									"netpath");
	if (clnt == (CLIENT *) NULL) {
		clnt_pcreateerror(host);
		exit(1);
	}
	printmessage_1_arg = 
							(char *) malloc(256);
	strcpy(printmessage_1_arg, "Hello World");

	retval_1 = printmessage_1(&printmessage_1_arg,
											&result_1,clnt);
	if (retval_1 != RPC_SUCCESS) {
		clnt_perror(clnt, "call failed");
	}
	printf("result = %d¥n", result_1);

	clnt_destroy(clnt);
}

main(argc, argv)
	int argc;
	char *argv[];
{
	char *host;

	if (argc < 2) {
		printf("usage:  %s server_host¥n", argv[0]);
		exit(1);
	}
	host = argv[1];
	messageprog_1(host);
}

ここで、rpcgen が生成したコードには、引数も戻り値もポインタで渡さなければならないことに注意してください。これはプログラムを再入可能にするために必要です。スタブ関数の戻り値は、遠隔手続きの呼び出しが正常終了したかエラーが起こったかを示します。正常終了した場合は、RPC_SUCCESS が返されます。例 3-16 に示すマルチスレッド対応のクライアント側スタブプログラム (-M で生成) とマルチスレッド対応でないクライアント側スタブプログラムを比較してください。マルチスレッド未対応のクライアント側スタブプログラムは、静的変数を使用して戻り値を格納し、一度に 1 つしかスレッドを使用することができません。


例 3-16 クライアント側スタブプログラム (マルチスレッドに対応していない)

int *
printmessage_1(argp, clnt)
	char **argp;
	CLIENT *clnt;
{
	static int clnt_res;
	memset((char *)&clnt_res, 0, 
								sizeof (clnt_res));
	if (clnt_call(clnt, PRINTMESSAGE,
		(xdrproc_t) xdr_wrapstring, 
										(caddr_t) argp,
		(xdrproc_t) xdr_int, (caddr_t) 
										&clnt_res,
		TIMEOUT) != RPC_SUCCESS) {
		return (NULL);
	}
	return (&clnt_res);
}

例 3-17 に、サーバ側コードを示します。


注 -

マルチスレッド対応モードを使用するサーバプログラムをコンパイルする場合は、スレッドライブラリをリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します。



例 3-17 マルチスレッド対応サーバ側スタブプログラム

#include "msg.h"
#include <syslog.h>

bool_t
printmessage_1_svc(argp, result, rqstp)
	char **argp;
	int *result;
	struct svc_req *rqstp;
{
	int retval;

	if (*argp == NULL) {
		syslog(LOG_INFO, "argp is NULL¥n");
		*result = 0;
	}
	else {
		syslog("argp is %s¥n", *argp);
		*result = strlen (*argp);
	}
	retval = 1;
	return (retval);
}

int
messageprog_1_freeresult(transp, xdr_result, result)
	SVCXPRT *transp;
	xdrproc_t xdr_result;
	caddr_t result;
{
	/*
	 * 必要に応じてメモリ解放のためのコードを挿入
	 */
	(void) xdr_free(xdr_result, result);
}

サーバ側のコードでは、静的変数を使用して戻り値を格納してはいけません。呼び出し側のルーチンから戻り値へのポインタが渡されますので、戻り値はそこに返します。正常終了の場合は 1 を返し、エラーが起こった場合は 0 を返します。

rpcgen が生成するコードには、手続きの呼び出しで割り当てたメモリを解放するルーチンの呼び出しも含まれています。メモリの不正使用を避けるため、サービスルーチンで割り当てたメモリはすべてそのルーチンで解放する必要があります。上の例では、messageprog_1_freeresult() でメモリの解放を行います。

通常は、xdr_free() を使用して割り当てたメモリを解放します。(上の例では、メモリ割り当てを行っていないので、メモリの解放は実行されません)。

-M フラグを -N-C のフラグと共に指定する例として、例 3-18add.x を見てみます。


例 3-18 マルチスレッド対応のプログラム : add.x

program ADDPROG {
	version ADDVER {	
	int add(int, int) = 1;
	} = 1;
}= 199;

このプログラムでは、2 つの数値を加えてその結果をクライアントに返します。次のコマンドで、このファイルに対して rpcgen を実行します。

% rpcgen -N -M -C add.x

このプログラムを呼び出す例 3-19は次のようになります。


例 3-19 マルチスレッド対応クライアント側プログラム : add.x

/*
 * このクライアント側メインルーチンでは複数のスレッドを起動します。
 * 各スレッドから同時にサーバルーチンを呼び出します。
 */

#include "add.h"

CLIENT *clnt;
#define NUMCLIENTS 5
struct argrec {
	int arg1;
	int arg2;
};

/* 
 * 現在実行中のスレッド数をカウント
 */
int numrunning;
mutex_t numrun_lock;
cond_t condnum;

void
addprog(struct argrec *args)
{
	enum clnt_stat retval;
	int result;
	/* サーバルーチンの呼び出し */
	retval = add_1(args->arg1, args->arg2,
											&result, clnt);
	if (retval != RPC_SUCCESS) {
		clnt_perror(clnt, "call failed");
	} else
		printf("thread #%x call succeeded,
					result = %d¥n", thr_getself(),
					result);
/*
 * 実行中のスレッド数をデクリメント
 */
	mutex_lock(&numrun_lock);
	numrunning--;
	cond_signal(&condnum);
	mutex_unlock(&numrun_lock);
	thr_exit(NULL);
}

main(int argc, char *argv[])
{
	char *host;
	struct argrec args[NUMCLIENTS];
	int i;
	thread_t mt;
	int ret;

	if (argc < 2) {
		printf("usage:  %s server_host¥n",
					argv[0]);
		exit(1);
	}
	host = argv[1];
	clnt = clnt_create(host, ADDPROG, ADDVER,
									"netpath");
	if (clnt == (CLIENT *) NULL) {
		clnt_pcreateerror(host);
		exit(1);
	};
	mutex_init(&numrun_lock, USYNC_THREAD, NULL);
	cond_init(&condnum, USYNC_THREAD, NULL);
	numrunning = 0;

	/* 個々のスレッドの起動 */
	for (i = 0; i < NUMCLIENTS; i++) {
		args[i].arg1 = i;
		args[i].arg2 = i + 1;
		ret = thr_create(NULL, NULL, addprog,
									(char *) &args[i],
				 					THR_NEW_LWP, &mt);
		if (ret == 0)
			numrunning++;
	}

	mutex_lock(&numrun_lock);
	/* 全スレッドの終了を待つ */
	while (numrunning != 0)
		cond_wait(&condnum, &numrun_lock);
	mutex_unlock(&numrun_lock);
	clnt_destroy(clnt);
}

サーバ側の手続きは例 3-20 のようになります。

注 -

マルチスレッド対応モードを使用するサーバ側プログラムをコンパイルする場合は、スレッドライブラリにリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します



例 3-20 マルチスレッド対応サーバ側プログラム : add.x

add_1_svc(int arg1, int arg2, 
						int *result, struct svc_req *rqstp)
{
	bool_t retval;
	/* 結果の計算 */
	*result = arg1 + arg2;
	retval = 1;
	return (retval);
}

/*
 * サーバ手続きで割り当てたメモリを解放するルーチン
 */
int
addprog_1_freeresult(SVCXPRT *transp,
									xdrproc_t xdr_result,
									caddr_t result)

{
	(void) xdr_free(xdr_result, result);
}

自動マルチスレッド対応モード

自動マルチスレッド対応モードにより、クライアントの要求を同時に処理するために Solaris スレッドが自動的に使用されます。-A オプションを指定して、RPC コードを自動マルチスレッド対応モードで生成します。また、-A を指定すると自動的に -M が指定されるため、-M を明示的に指定する必要はありません。生成されたコードはマルチスレッド対応でなければならないため、-M が (明示的ではなくても) 必要です。

マルチスレッド対応 RPC の詳細については 「マルチスレッド RPC プログラミング」、および 「マルチスレッド自動モード」 を参照してください。

次に、rpcgen によって生成される自動モードのプログラムの例を示します。例 3-21 は、rpcgen のプロトコルファイルである time.x のコードです。文字列は遠隔手続きに引き渡されます。遠隔手続きは、文字列を表示してクライアントの文字列長を返します。マルチスレッド対応スタブを生成するには、次のコマンドを実行します。


例 3-21 自動マルチスレッド対応モード : time.x

		program TIMEPROG {
			version TIMEVERS {
				unsigned int TIMEGET(void) = 1;
				void TIMESET(unsigned) = 2;
			} = 1;
		} = 0x20000044;

% rpcgen -A time.x


注 -

-A オプションを使用すると、生成されたサーバ側のコードには、サーバの自動マルチスレッド対応モードを使用するための命令が含まれます。

マルチスレッド対応モードを使用するサーバ側プログラムをコンパイルする場合は、スレッドライブラリにリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します。


TI-RPC または TS-RPC のライブラリ選択

旧バージョンの rpcgen では、ソケット関数を使用してスタブプログラムを作成していました。SunOS 5.4 では、トランスポート独立の RPC ルーチン (TI-RPC) か、特定のトランスポート固有のソケットルーチン (TS-RPC) のどちらを使用するか選択できます。この機能は、旧バージョンとの互換性を保つために提供されています。デフォルトでは TI-RPC ルーチンが使用されます。TS-RPC ルーチンを使用したソースコードを生成するには、rpcgen-b フラグを指定します。

ANSI C に準拠したコードの生成

rpcgen では、ANSI C に準拠したコードを出力するか、SPARCompiler C++3.0 に準拠したコードを選択するか指定できます。ANSI C に準拠したコードを生成するには、-C フラグを指定します。ほとんどの場合、「C 形式モード」指定フラグ -N も同時に指定します。

add.x のサーバ側テンプレート例は、次のコマンドで生成できます。

rpcgen -N -C -Ss -o add_server_template.c add.x

ここで、C++ 3.0 で記述されたサーバ上では遠隔手続き名が接尾辞 _svc で終わっていなければならないことに特に注意してください。次の例では、add.x に対して、コンパイルフラグ -C を指定してクライアント側の add_1 とサーバ側の add_1_svc が生成されています。


例 3-22 ANSI C に準拠したサーバ側テンプレート

/*
 * このファイルはテンプレートです。これを基にしてユーザ独自の関数を
 * 作成してください。
 */
#include <c_varieties.h>
#include "add.h"

int *
add_1_svc(int arg1, int arg2,
					struct svc_req *rqstp)
{
	static int result;
	/*
	 * ここにサーバプログラムのコードを挿入
	 */
	return(&result);
}

この出力ファイルは、構文も構造も ANSI C に準拠しています。-C フラグを指定して生成したヘッダファイルは、ANSI C でも SPARCompiler C++ でも使用できます。

xdr_inline() カウント

rpcgen は、可能な限り xdr_inline() (xdr_admin(3) マニュアルページを参照) を使用して、より効率の良いコードを生成しようとします。構造体の中に xdr_inline() を使用できるような要素 (たとえば、integerlongbool) があれば、構造体のその部分は xdr_inline() を使用してパックされます。デフォルトでは、パックされる要素が 5 つ以上連続していれば、インラインコードが生成されます。-i フラグを使用してインラインコードを生成する個数を変更することができます。たとえば、次のコマンド

rpcgen -i 3 test.x

では、パックできる要素が 3 つ以上連続していれば、インラインコードが生成されます。次のコマンド

rpcgen -i 0 test.x

では、インラインコードの生成が禁止されます。

ほとんどの場合、-i フラグを指定する必要はありません。このフラグの対象となるのは _xdr.c スタブプログラムだけです。