遠隔手続き呼び出し(RPC)の基本概念

情報学類 分散システム					2008年12月25日

                                       筑波大学システム情報工学研究科
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/dsys-2008/2008-12-25
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■今日の大事な話

谷口 秀夫 (編), 谷口 秀夫, 佐藤一朗, 佐藤 文明, 柴田 義孝, 新城 靖, 横山 和俊 (著): "情報処理学会編集 IT Text 分散処理", オーム社 (2005年9月). ISBN: 4274201333.

第2章 基盤技術 (新城) 2.6節 遠隔手続き呼出し

■クライアント・サーバ・モデル

プロセス間通信を構造化したもの。 クライアント・サーバ・モデルに基づく通信の実装方法 クライアント・サーバ・モデルに基づくプロセス間通信のうち、 手続き呼出しの形に見えたら RPC (Remote Procedure Call)。

■遠隔手続き呼び出し(RPC)

手続き呼出しの形でプロセス間通信を行う方法。 1984年 Birrel and Nelson。

◆スタブによるRPCの実現

例:手続き put()。ハッシュ表にデータを格納する手続きで、引数にキーとな る文字列と値となる整数を取る。

クライアントのmain()、クライアント側スタブ、サーバ側スタブ、サーバput()手続き。

図? スタブによるRPCの実現

特徴 スタブの働きで、クライアント側のプロセスとサーバ側のプロセスは、自動的 にプロセス間通信を行うことができる。
クライアント側スタブ
サーバ側スタブ

◆スタブの作り方

◆スタブの自動生成

インタフェース記述の例: ハッシュ表
typedef string key_t<256>;
struct keyvalue_t { 
   key_t key; 
   int   value ;
};
typedef key_t  keyarray_t<>;

program HASHTABLE_PROG { 
   version HASHTABLE_VERSION {
       int        PUT(keyvalue_t)  = 11 ; 
       int        GETVALUE(string) = 12 ; 
       keyarray_t GETKEYS(void)    = 13 ; 
   } = 1 ;
} = 0x20051001 ;
インタフェース定義をスタブ生成器に与えると、クライアント側スタブ、サー バ側スタブが自動的に生成される。

インタフェース記述の内容

自動生成されるもの

◆遠隔手続き呼び出しのまとめ

  1. ある手続きがクライアント側スタブを通常の方法で呼び出す。
  2. クライアント側スタブは、引数を整列化することでネットワーク用のメッ セージを作成し、そのローカルのオペレーティング・システムを呼び出す。
  3. クライアント側のオペレーティング・システムは、メッセージをリモート のサーバ側のオペレーティング・システムに送る。
  4. サーバ側のオペレーティング・システムは、メッセージをサーバ側のスタ ブに渡す。
  5. サーバ側のスタブは、メッセージを非整列化することで引数を取り出し、 サーバ側の手続きを呼び出す。
  6. サーバ側の手続きは、その仕事を行い、結果をサーバ側スタブに返す。
  7. サーバ側スタブは、結果を整列化することでネットワーク用のメッセージ を作成し、そのローカルのオペレーティング・システムを呼び出す。
  8. サーバ側のオペレーティング・システムは、そのメッセージをクライアン トに送る。
  9. クライアント側のオペレーティング・システムは、メッセージをクライア ント側スタブに渡す。
  10. クライアント側スタブは、メッセージを非整列化して、結果を取り出し、 その結果を呼び出された手続きに返す。

◆遠隔手続き呼び出しと通常の手続き呼出しの違い

遠隔手続き呼び出しの意味「意味(semantics)」を、通常の手続き呼出しと同じ (透明)にしたい。しかし、完全に同じ「意味」を提供にすることは難しい。

◆通常の手続き呼出し

プログラミング言語における手続き呼出しでの引数の渡し方 プログラム言語によって、方法が違う。
intなど基本型 構造体 オブジェクト 配列
C言語 - 自動ポインタ値化*
Java - 参照 参照
* C言語の配列を「参照呼び」と説明している教科書もある。

言語が持つ意味を、RPCでは再現できないことがある。

◆call-by-value

C言語の通常の方式。変数の値のコピーが渡される。
   1:	#include <stdio.h>
   2:	
   3:	int square( int a ) {
   4:	    a =  a * a ;
   5:	    return( a );
   6:	}
   7:	
   8:	main() {
   9:	    int x, y, result;
  10:	    x = 10;
  11:	    y = 20;
  12:	    result = square( x );
  13:	    printf("x==%d, result==%d\n", x, result);
  14:	    result = square( y++ );
  15:	    printf("y==%d, result==%d\n", y, result);
  16:	}
実行結果
% make square-value [←]
cc     square-value.c   -o square-value
% ./square-value  [←]
x==10, result==100
y==21, result==400
% []

◆call-by-reference

C++ の参照型は、call-by-reference に近い。
   1:	#include <stdio.h>
   2:	
   3:	int square( int &a ) {
   4:	    a =  a * a ;
   5:	    return( a );
   6:	}
   7:	
   8:	main() {
   9:	    int x, y, result;
  10:	    x = 10;
  11:	    y = 20;
  12:	    result = square( x );
  13:	    printf("x==%d, result==%d\n", x, result);
  14:	//  result = square( y++ );
  15:	//  printf("y==%d, result==%d\n", y, result);
  16:	}
実行結果。引数で渡した変数 x の値が書き換えられている。
% make square-ref [←]
g++     square-ref.cc   -o square-ref
% ./square-ref  [←]
x==100, result==100
% []
C++の参照型では、引数には、変数しかかけない。y++ のような式では、コンパ イル時にエラーになる。
% cat -n square-ref.cc [←]
     1  #include <stdio.h>
     2
     3  int square( int &a ) {
     4      a =  a * a ;
     5      return( a );
     6  }
     7
     8  main() {
     9      int x, y, result;
    10      x = 10;
    11      y = 20;
    12      result = square( x );
    13      printf("x==%d, result==%d\n", x, result);
    14      result = square( y++ );
    15      printf("y==%d, result==%d\n", y, result);
    16  }
% make square-ref [←]
g++     square-ref.cc   -o square-ref
square-ref.cc: In function 'int main()':
square-ref.cc:14: error: invalid initialization of non-const reference of type 'int&' from a temporary of type 'int'
square-ref.cc:3: error: in passing argument 1 of 'int square(int&)'
make: *** [square-ref] Error 1
C++の参照型は、プログラムが読みにくくなるので使ってはいけない。
main() {
   int x ;
   x = 10 ;
   f( x );
   printf("%d\n",x);
}
C言語のレベルでは、f(x) と呼んでも、決して x の値は、変化しない。C++の 参照型を使えば、f() の型宣言を見ないと、変化するか変化しないかわからな い。「型宣言を見る」という手間の分だけ、プログラムが読みにくくなる。 全部の関数について、見ていたら疲れる。

◆ポインタ

「参照」は、ポインタに似ている。ただし、C言語で渡されるのは、ポインタ のコピー(値)が渡される。
   1:	#include <stdio.h>
   2:	
   3:	int square( int *a ) {
   4:	    *a =  *a * *a ;
   5:	    return( *a );
   6:	}
   7:	
   8:	main() {
   9:	    int x, y, result, *xp, *yp;
  10:	    x = 10; xp = &x;
  11:	    y = 20; yp = &y;
  12:	    result = square( xp );
  13:	    printf("x==%d, result==%d, &x==0x%x, xp==0x%x\n", 
  14:	           x, result, &x, xp);
  15:	    result = square( yp++ );
  16:	    printf("y==%d, result==%d, &y==0x%x, yp==0x%x\n", 
  17:	           y, result, &y, yp);
  18:	}
% make square-pointer [←]
cc     square-pointer.c   -o square-pointer
% ./square-pointer  [←]
x==100, result==100, &x==0xbffff534, xp==0xbffff534
y==400, result==400, &y==0xbffff538, yp==0xbffff53c
% []
C++の参照型は、プログラムが読みにくくなるので使ってはいけないが、 C言語のポインタ渡しは、使ってもよい。
main() {
   int x ;
   x = 10 ;
   f( &x );
   printf("%d\n",x);
}
C言語のレベルでは、f(&x) と&を付けた段階で、 f() の内容を知らなくてもx の値が変化することが推察される。値が変化する 可能性について、ポインタを渡している関数だけ注意すればよい。

◆配列

C言語では、配列を引数に渡す場合には、自動的にポインタ(先頭要素の番地) に変換される。 次のプログラムでは、どれも関数 f() に同じ値(配列の先頭番地)を渡してい る。配列の名前を書いても、& を書いたものと同じになる。
main() {
   int a[10] ;
   f( a );
   f( &a );
   f( &a[0] );
}
配列を自動的にポインタに変換する歴史的な理由

◆マクロ展開

call-by-name は、C言語のマクロ展開に近い意味がある。
   1:	#include <stdio.h>
   2:	
   3:	#define square( a ) ((a)*(a))
   4:	
   5:	main() {
   6:	    int x, y, result;
   7:	    x = 10;
   8:	    y = 20;
   9:	    result = square( x );
  10:	    printf("x==%d, result==%d\n", x, result);
  11:	    result = square( y++ );
  12:	    printf("y==%d, result==%d\n", y, result);
  13:	}
実行結果。y++ と 1 度書いただけなのに、2 増えている。
% make square-macro [←]
cc     square-macro.c   -o square-macro
% ./square-macro  [←]
x==10, result==100
y==22, result==400
% []
マクロ展開だけして止めてみるとわかる。
% cc -E square-macro.c > square-macro.i [←]
% wc  square-macro.i [←]
     421    1135    9164 square-macro.i
% tail -13 square-macro.i  [←]
# 2 "square-macro.c" 2



main() {
    int x, y, result;
    x = 10;
    y = 20;
    result = ((x)*(x));
    printf("x==%d, result==%d\n", x, result);
    result = ((y++)*(y++));
    printf("y==%d, result==%d\n", y, result);
}
% []

◆call-by-value-restore

古い Fortran の実装。コンパイル時に変数がすべて決まる。スタックに引数を 置かなくてもよい。再帰がなかったので、そもそもスタックが不要で、戻り番 地を手続きごとの変数に書いていた。
   1:	#include <stdio.h>
   2:	
   3:	int square_a;
   4:	int square() {
   5:	    square_a = square_a * square_a ;
   6:	    return( square_a );
   7:	}
   8:	
   9:	main() {
  10:	    int x, y, result;
  11:	    x = 10;
  12:	    y = 20;
  13:	    square_a = x ; result = square(); x = square_a ;
  14:	    printf("x==%d, result==%d\n", x, result);
  15:	    square_a = y++ ; result = square(); y = square_a ;
  16:	    printf("y==%d, result==%d\n", y, result);
  17:	}
実行結果。
% make square-copy-restore [←]
cc     square-copy-restore.c   -o square-copy-restore
% ./square-copy-restore  [←]
x==100, result==100
y==400, result==400
% []

◆RPCの基本的な考え方と制約

この制約から、インタフェースを変えなければならないことがある。

◆例:RPCでread()

例: ファイルの内容を読むシステム・コール read()
ssize_t read(int fd, void *buf, size_t nbytes)
buf は、結果を受け取る場所を示したもので、RPCで遠隔に送る意味は ない。

方法1。インタフェースを変える。

struct read_result_t {
       ssize_t read_bytes;
       void *buf;
};
read_result_t read(int fd, size_t nbytes)
方法2: スタブで違いを吸収する。
ssize_t read(int fd, void *buf, size_t nbytes)
{
	fd, buf(nbytes分), nbytes を整列化する。
	サーバへ要求メッセージとして送る。
	サーバから応答メッセージを受け取る。
	応答メッセージを非整列化して、結果の read_bytes と読んだ内容を取り出す。
	読んだ内容を buf read_bytes 分へコピーする。
	return read_bytes ;
}
単純に行えば、上のように余計なコピーが入ることがある。 スタブで最適化すれば、クライアントからサーバへのコピーを減らせる。

◆SunRPCでのポインタの扱い

SunRPC では、ポインタの先の1要素だけコピーして送る機能がある。 この機能を使って遅れるもの。 送れないもの

◆バインディング

バインディングとはクライアントとサーバの対応関係を、個々 の手続き呼出しの前に決定すること。

クライアント3個、サーバ2個、対応関係を決める。

図? RPCのバインディング

クライアント側のプログラムとサーバ側のプログラムの対応はもはや1対1で はない。

◆サーバの複製(replication)

RPCのサーバを複数用意することが考えられる。 うまく作れば、あるサーバが落ちていても、別のサーバで対応できる。 バインディング時に、動いているサーバを選択する。

◆RPCの実装例

オブジェクト指向の考え方が含まれると、ORB (Object Request Broker) や分散オブジェクトと呼ばれることもある。

◆RPCの利点

単なるメッセージ・パッシングと比較して RPC の問題点(単なるメッセージ・パッシングの利点) ただし、自由度は、プログラムが難解になり、開発のコストを上げることがあ る。クライアント・サーバ・モデルに従うなら、RPCの方がよい。

XML-RPC, SOAPなど、テキストを使う RPC もある。


Last updated: 2008/12/23 14:31:19
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>