RPCと障害回復

並行システム

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

このページは、次の URL にあります。
http://www.cs.tsukuba.ac.jp/~yas/sie/csys-2009/2010-01-08
あるいは、次のページから手繰っていくこともできます。
http://www.cs.tsukuba.ac.jp/~yas/sie/
http://www.cs.tsukuba.ac.jp/~yas/

■連絡

1月15日(金)は、全学(学群、大学院)臨時休業。センター試験のため。

■RPC

RPC (Remote Procedure Call) ( 遠隔手続き呼び出し ) は、分散システムを構築する時に広く使われている「プロセス間通信」の方法。 NFS (Network File System) を始めとする分散ファイル・システムの構築や、 や NIS (Network Information Service) (パスワード・ファイルなどの共有) で使われている。

(<−>文化的には、TCP/IP上のテキストベースのプロトコルとは対照。)

◆RPCの特徴

TCP/IP で提供されているストリームや UDP/IP で提供されているデー タグラムと比較して、RPC には次のような特徴がある。 RPCで「遠隔(remote)」というのは、もともとはネットワークに接続された別 のコンピュータという意味であったが、最近では、同じコンピュータの内部で もRPCが使われる。「遠隔(remote)」とは、「別のコンピュータ」という意味 ではなく、「別のアドレス空間」という意味もある。

RPCでは、別のアドレス空間の間でデータがやり取りされるので、基本的に 「ポインタ」を受け渡しすることはできない。しかし、SunRPC では、ポイン タの先を再帰的に「コピー」する機能がある。

◆スタブとスタブ生成器

RPCでプログラムを作成するときには、3つの基本命令を利用することは、ほ とんどない。 スタブ生成器(stub generator) を使えば、インタフェースの定義から3つの基本命令を呼び出すようなプログ ラムが自動生成される(lex、yacc )。

スタブ(stub) は、プロセス間通信を普通の手続き呼出しと全く同じ形式で行なうことができ るようにするためのプログラム。 もともとは木の切株の意味。

図? クライアント側スタブ、サーバ側スタブ

図? スタブによる遠隔手続き呼出しの実現

スタブの分類

クライアント側スタブ
手続き呼出しの形で呼び出される。do_operation() 命令を実行する。
サーバ側スタブ
無限ループを持つプログラム。 get_request() でクライアントからのメッ セージを受け取り、対応する手続きを呼び出し、結果をsend_reply() により 返す。
スタブでは、パラメタ(引数と結果)の 整列化(marshalling) ( パック ) と 非整列化(unmarshalling) ( アンパック ) も行なわれる。SunRPC では、 XDR (eXternal Data Representation) と呼ばれている方法を使っている。

◆RPCを実装するメッセージ ☆

例1:7種類のメッセージを使う方法

例2:3種類(回数)

例3:2種類(回数)

◆SunRPC

SunRPC は、Sun Microsystems社により開発され、仕様やソースコードが 公開された RPC の実装。ONC RPC (Open Network Computing) とも呼ばれる。 RFC にもなっている。

◆SunRPC のrpcgenコマンドとファイル

SunRPC には、rpcgen というスタブ・コンパイラがある。 rpcgen コマンドを使うには、次のようなファイルを作成する。

rpcgenによるRPCプログラム開発で利用するファイル

図? rpcgenによるRPCプログラム開発で利用するファイル

name.x
インタフェースを記述。
name_client.c
クライアント側の main プログラム。
name_server.c
サーバ側で、RPC で呼び出されるプログラム。 (main は、rpcgen により自動生成される。)

◆rpcgenコマンドの使い方

% rpcgen name.x [←]
次の4つのファイルが生成される。
name.h
そのRPCのプログラムで使う定数、データ構造、スタブ手続きのインタフェー ス。
name_clnt.c
クライアント側のスタブ。
name_xdr.c
name.x で定義したデータ構造について、 XDR のための手続き(整列化と非整列化を行なう手続き) 。 クライアント側とサーバ側の両方で使われる。
name_svc.c
サーバ側の main 関数とディスパッチ手続き。受け付けた RPC の要求を解析 して、開発者が定義した手続きを呼び出す。
これらのファイルの内容は、人間が十分読めるも。

◆rpcgenのインタフェース定義の例

ハッシュ表 typedef char *key_t; typedef key_t *keyarray_t;
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(key_t)  = 12 ; 
       keyarray_t GETKEYS(void)    = 13 ; 
   } = 1 ;
} = 0x20051001 ;
遠隔手続き呼出しでは、送受信できるデータは基本的には値だけであり、 ポインタを送ることはできない。

SunRPC では、ポインタの先の1要素だけコピーして送る機能がある。SunRPC では、ポインタによる単純なリストや木構造を送ることができる。

双方向リストなど、内部にループを含むものは SunRPC では送ることができな い。また、ポインタで実現された有向非循環グラフを送ると木構造に展開され てしまう。

インタフェース定義から、次のようなファイルが生成される。

◆自動生成されるクライアント側スタブの例 ☆

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "hashtable.h"
#ifndef _KERNEL
#include <stdio.h>
#include <stdlib.h> /* getenv, exit */
#endif /* !_KERNEL */

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

int *
put_1(argp, clnt)
	keyvalue_t *argp;
	CLIENT *clnt;
{
	static int clnt_res;

	memset((char *)&clnt_res, 0, sizeof (clnt_res));
	if (clnt_call(clnt, PUT,
		(xdrproc_t) xdr_keyvalue_t, (caddr_t) argp,
		(xdrproc_t) xdr_int, (caddr_t) &clnt_res,
		TIMEOUT) != RPC_SUCCESS) {
		return (NULL);
	}
	return (&clnt_res);
}

◆Binding

クライアントとサーバを結び付ける。 RPC では、動的(dynamic)になる。

ローカルの手続き呼出しでは、リンク時に固定される。

クライアントとサーバは1対1ではない。

binding のための命令

◆SunRPC での Binding

SunRPCでは、多くの場合、TCP/IPやUDP/IPを使ってメッセージを送る。この場 合、手続きをを次の5つの番号で区別する。 広く使われるプログラム番号は、 /etc/rpc というファイルに含まれている。

TCP/IPまたはUDP/IPでデータを送るにはポート番号が必要になる。 サーバが動作しているホストには、 portmapper とよばれる特殊な RPC のサーバが動作している。 サーバは起動時に自分の<プログラム番号, バージョン, プロトコル, ポート番号>をPortmapper に登録する (pmap_set())。

クライアントは、実際にサーバに接続する前に、Portmapper に<プ ログラム番号, バージョン, プロトコル>を送り、 TCP/IPまたはUDP/IPのポート番号を得る(pmap_getport())。最終的には, この ポート番号を使ってメッセージを送る。

Portmapper 自身のポート番号は、111 に固定されている。

portmapper が binding

図? SunRPC での binding (portmapper)

■PRCにおける障害対策 ☆

◆障害

portmapper が binding

図? RPCで起こりうる障害

◆クライアントがサーバを見つけられない

◆要求メッセージが紛失

時間切れ再要求。簡単。

◆応答メッセージが紛失

難しい。単純な時間切れだとまずい。

例: 銀行預金転送。

対策:

◆要求受信後、サーバがクラッシュ

番号では対応できない。要求実行前と実行後を区別できない。

RPCのセマンティクス

集中だと、クライアントもサーバもいっしょに死ぬので、問題はない。

◆要求送信後、クライアントがクラッシュ

孤児問題(orphan problem)。

親(クライアント)がいない計算を孤児という。

対応方法

根絶(extermination)。
RPC 前にログに書く。クラッシュしたらログを見て根絶する。重たい。 孫の孤児(grandorphan)問題がある。
再生(reincarnation)
リブートすると、タイムスタンプをサーバに投げる。サーバは、 全孤児を消去する。
穏和な再生(gentle reincarnation)
タイムスタンプを受信すると、サーバは親を探し、見つからない時だけ孤児を消去する。
期限切れ(expiration)
RPC に標準時間Tを与え、その時間内に終了しないものを消去する。 それ以上かかる時には、明示的に要求する。クラッシュ後、サーバがTだけ待てば 孤児は消える。

孤児が、ロックを持っていたら、孤児を消しただけでは話を終わらない。

◆SunRPC のクライアント側で見えるエラー

Linux, /usr/include/rpc/clnt.h
/*
 * Rpc calls return an enum clnt_stat.  This should be looked at more,
 * since each implementation is required to live with this (implementation
 * independent) list of errors.
 */
enum clnt_stat {
        RPC_SUCCESS=0,                  /* call succeeded */
        /*
         * local errors
         */
        RPC_CANTENCODEARGS=1,           /* can't encode arguments */
        RPC_CANTDECODERES=2,            /* can't decode results */
        RPC_CANTSEND=3,                 /* failure in sending call */
        RPC_CANTRECV=4,                 /* failure in receiving result */
        RPC_TIMEDOUT=5,                 /* call timed out */
        /*
         * remote errors
         */
        RPC_VERSMISMATCH=6,             /* rpc versions not compatible */
        RPC_AUTHERROR=7,                /* authentication error */
        RPC_PROGUNAVAIL=8,              /* program not available */
        RPC_PROGVERSMISMATCH=9,         /* program version mismatched */
        RPC_PROCUNAVAIL=10,             /* procedure unavailable */
        RPC_CANTDECODEARGS=11,          /* decode arguments error */
        RPC_SYSTEMERROR=12,             /* generic "other problem" */
        RPC_NOBROADCAST = 21,           /* Broadcasting not supported */
        /*
         * callrpc & clnt_create errors
         */
        RPC_UNKNOWNHOST=13,             /* unknown host name */
        RPC_UNKNOWNPROTO=17,            /* unknown protocol */
        RPC_UNKNOWNADDR = 19,           /* Remote address unknown */

        /*
         * rpcbind errors
         */
        RPC_RPCBFAILURE=14,             /* portmapper failed in its call */
#define RPC_PMAPFAILURE RPC_RPCBFAILURE
        RPC_PROGNOTREGISTERED=15,       /* remote program is not registered */
        RPC_N2AXLATEFAILURE = 22,       /* Name to addr translation failed */
        /*
         * unspecified error
         */
        RPC_FAILED=16,
        RPC_INTR=18,
        RPC_TLIERROR=20,
        RPC_UDERROR=23,
        /*
         * asynchronous errors
         */
        RPC_INPROGRESS = 24,
        RPC_STALERACHANDLE = 25
};

◆idempotent冪等

冪等(idempotent)な操作とは、 その操作を何回繰り返しても、1回だけ実行した時と同じ結果になるもの。

例:

idempotentではない操作

◆stateless server

無状態サーバ(stateless server) とは、サーバ内部に状態を保持しないようなサーバ。

状態の例

RPCで冪等な操作や無状態サーバを実現すると、クラッシュに強いシステムを 作れる。クライアントは、サーバから応答がない場合、何度要求を再送信して もよい。

例:NFS Version 2

サーバは、ファイルに対する書き込み要求を受け付けると、ディスクへ の書き込みを完了してから応答を返す。 応答が返ってきた要求は、 ディスクへの書き込みが完了したことが保証されている。 この段階でサーバがクラッシュしたとしても、なにも失われない。

しかし、、、重たい。NFS Version 3 では、状態付きのサーバになった。

■NFS

NFS ( Network File System ) は, Sun Microsystems 社が 開発したネットワーク・ファイル・システムの名前(固有名詞, 商標)。

ネットワーク・ファイル・システム(一般名詞)
ネットワークを通じて他のコンピュータ上にあるファイルを あたかも自分自身のローカルディスクにあるファイ ルと同じように扱えるようにしたファイルシステム
分散ファイルシステム
ネットワーク・ファイル・システムが発展して 「分散透明性(分散透過性、network transparency)」 が実現されたもの。
NFS は、Unix 系の OS (MacOSX 含む) では、事実上の標準。

その他のネットワーク・ファイル・システム(用のプロトコル)

◆NFSの機能

NFSを使うと, ネットワークを通じて別のコンピュータ上のファイルシステム の一部分を, ローカルディスク上にあるファイルシステムと同じように, 自分 のファイルシステムの木に マウント(mount) できる。

図? NFSによるファイルの共有

図? NFSによるファイルの共有

相互に参照し合える。

表? NFSで使われているRPCの手続き

手続き名 意味 関連するコマンド、 システムコール
null() 何もしない rpcinfo -u hostname nfs コマンド
getattr() 属性の読み出し ls -l コマンド, stat システムコール , open システムコール
setattr() 属性の設定 chmod , chown コマンド
lookup() ファイルの検索 open システムコール
readlink() シンボリックリンクの読み出し ls -l コマンド, readlink システムコール
read() ファイルの読み出し read システムコール
write() ファイルの書き込み write システムコール
create() ファイルの作成 creat システムコール, open システムコール
remove() ハードリンクの削除 rm コマンド, unlink システムコール
rename() ファイル名前の変更 mv コマンド, rename システムコール
link() ハードリンクの作成 ln コマンド, link システムコール
symlink() シンボリックリンクの作成 ln -s コマンド, symlink システムコール
mkdir() ディレクトリの作成 mkdir コマンド
rmdir() ディレクトリの削除 rmdir コマンド
readdir() ディレクトリの読み出し ls コマンド
statfs() ファイルシステムの利用状況 df コマンド, statfs システムコール
commit()* ディスクへの書き込み fsync システムコール
access()* アクセス権のチェック access システムコール
open()** ファイルを開く。
close()** ファイルを閉じる。
lock()** ファイルのロック。
renew()** ファイルのロックの更新。
compound()** 複合手続き。複数の手続きをまとめて実行する。

* は、NFS Version 3 で追加された手続き。

** は、NFS v4 で追加された手続き ☆。

◆NFSファイルハンドル

NFS でファイルやディレクトリを区別するための識別子。32バイト。

const NFS_FHSIZE	= 32;
...
/*
 * File access handle
 */
struct nfs_fh {
	opaque data[NFS_FHSIZE];
};

一番最初のNFSファイル・ハンドルをどうやって入手するか。

◆NFSマウントのためのRPCプログラム

一番 NFS v2, NFS v3 では、NFS 本体とは別にディレクトリ木のルートを得るため のRPC のプログラム(MOUNTPROG)がある。 /usr/include/rpcsvc/mount.x
手続き名 意味 関連するコマンド、 システムコール
null() 何もしない rpcinfo -u hostname mount コマンド
mnt() NFSファイルハンドルを返す mount コマンド
dump() マウント一覧表 showmount hostname コマンド
umnt() アンマウント umount コマンド
umntall() 全アンマウント umount -h hostname コマンド
export() アクセス可能なディレクトリのリストを返す  

◆lookup-read-write

NFS (v2,v3) には、次の手続きがない。 open() -- while(...) {read() or write()} -- close() のようなプログラムは、 NFS のレベルでは、lookup(); while( ... ) {read() or write()} になる。 read(), write() の引数は、必ずファイル中の読み書きする位置が含まれる。

◆lookup()

引数
検索対象のディレクトリの NFSファイルハンドルとファイル名
結果
ファイル、または、ディレクトリの NFS ファイル・ハンドルと属性
ファイル名には、区切り「/」は含まれない。要素ごとに lookup する。

2.2.5.  Look Up File Name

	diropres
	NFSPROC_LOOKUP(diropargs) = 4;

If the reply "status" is NFS_OK, then the reply "file" and reply
"attributes" are the file handle and attributes for the file "name"
in the directory given by "dir" in the argument.

2.3.10.  diropargs

    struct diropargs {
	fhandle  dir;
	filename name;
    };

The "diropargs" structure is used in directory operations.  The
"fhandle" "dir" is the directory in which to find the file "name".
A directory operation is one in which the directory is affected.

2.3.11.  diropres

    union diropres switch (stat status) {
    case NFS_OK:
	struct {
	    fhandle file;
	    fattr   attributes;
	} diropok;
    default:
	void;
    };

The results of a directory operation are returned in a "diropres"
structure.  If the call succeeded, a new file handle "file" and
the "attributes" associated with that file are returned along with
the "status".

◆read()

引数
対象ファイルの NFSファイルハンドル、読み込む位置(先頭からのオフセット)、バイト数
結果
属性、データ(opaque型)
2.2.7.  Read From File

	struct readargs {
		fhandle file;
		unsigned offset;
		unsigned count;
		unsigned totalcount;
	};

	union readres switch (stat status) {
	case NFS_OK:
		fattr attributes;
		nfsdata data;
	default:
		void;
	};

	readres
	NFSPROC_READ(readargs) = 6;

Returns up to "count" bytes of "data" from the file given by "file",
starting at "offset" bytes from the beginning of the file.  The first
byte of the file is at offset zero.  The file attributes after the
read takes place are returned in "attributes".

Notes:  The argument "totalcount" is unused, and is removed in the
next protocol revision.

◆write()

引数
対象ファイルの NFSファイルハンドル、書き込む位置(先頭からのオフセット)、データ(バイト数数含む)
結果
属性
2.2.9.  Write to File

	struct writeargs {
		fhandle file;
		unsigned beginoffset;
		unsigned offset;
		unsigned totalcount;
		nfsdata data;
	};

	attrstat
	NFSPROC_WRITE(writeargs) = 8;

Writes "data" beginning "offset" bytes from the beginning of "file".
The first byte of the file is at offset zero.  If the reply "status"
is NFS_OK, then the reply "attributes" contains the attributes of the
file after the write has completed.  The write operation is atomic.
Data from this "WRITE" will not be mixed with data from another
client's "WRITE".

Notes:  The arguments "beginoffset" and "totalcount" are ignored and
are removed in the next protocol revision.

◆cookie

RPC のようにコネクションが作られない通信サービスを使う時に冪等や無状態 といった性質を実現する時に必要になる技術。

例:NFSでのディレクトリの読み込み手続き nfsproc_readdir() で、1回の RPC で全部のデータを返せないことが起きる。 ディレクトリのどの位置まで読み込んだかを 示す中間状態を クッキー(cookie) という形でクライアントに返す。

クライアントは、次の RPC の呼び出しで、 前回受けとった応答の中のクッキーを、サーバへの要求に含めて送す。

◆readdir()

ls コマンドは、opendir() ライブラリ関数、getdirentries() システムコール (MacOSX, FreeBSD) を経て、NFS のレベルでは、readdir() になる。
引数
対象ディレクトリの NFSファイルハンドル、クッキー、バイト数
結果
エントリのリスト
各エントリは、名前、クッキー、inode番号からなる。
const NFS_COOKIESIZE	= 4;
typedef opaque nfscookie[NFS_COOKIESIZE];
2.2.17.  Read From Directory

	 struct readdirargs {
		 fhandle dir;
		 nfscookie cookie;
		 unsigned count;
	 };

	 struct entry {
		 unsigned fileid;
		 filename name;
		 nfscookie cookie;
		 entry *nextentry;
	 };

	 union readdirres switch (stat status) {
	 case NFS_OK:
		 struct {
			 entry *entries;
			 bool eof;
		 } readdirok;
	 default:
		 void;
	 };

	 readdirres
	 NFSPROC_READDIR (readdirargs) = 16;

 Returns a variable number of directory entries, with a total size of
 up to "count" bytes, from the directory given by "dir".  If the
 returned value of "status" is NFS_OK, then it is followed by a
 variable number of "entry"s.  Each "entry" contains a "fileid" which
 consists of a unique number to identify the file within a filesystem,
 the "name" of the file, and a "cookie" which is an opaque pointer to
 the next entry in the directory.  The cookie is used in the next
 READDIR call to get more entries starting at a given point in the
 directory.  The special cookie zero (all bits zero) can be used to
 get the entries starting at the beginning of the directory.  The
 "fileid" field should be the same number as the "fileid" in the the
 attributes of the file.  (See section "2.3.5. fattr" under "Basic
 Data Types".)  The "eof" flag has a value of TRUE if there are no
 more entries in the directory.

nfsproc_readdir() で、1回目と2回目の RPC の間にディレクトリの内容が 更新された場合、どのような結果になるのか不明。

◆NFS非同期入出力デーモン ☆

NFS非同期入出力デーモン ( nfsiod (local NFS asynchronous I/O Daemon) または biod (asynchronous Block I/O Daemon) ) は、NFSのクライアントホスト上で動き、NFSの非同期的な入出力を行う。

◆NFS lockdとrstatd ☆

NFS v2, NFS v3 には、ロックの機能が元々存在しない。次のような障害に対応 することが簡単ではない。 後に lockd と statd という2つの外付けのプログラムでロックの機能を付加し た(他の実装もある)。

クライアント側の lockd

サーバ側の lockd サーバ側の statd クライアント側の statd

図? NFS-client-lockd-statd-kernel NFS-server-lockd-kernel

図? NFSにおけるlockd と statd の役割

◆NFS Version 3

◆NFS Version 4

2003年

■練習問題

★問題(501) AYAとIAAの働き

RPCを実装するメッセージで 述べた AYA と IAA は、なぜ必要か。簡単に説明しなさい。

★問題(502) RPCにおける障害

自動生成されるクライアント側スタブ の例 で示した put_1() では、TIMEOUT で指定された時刻が経過してもサーバ から応答が返らなかった場合に、エラーになる。この時、どのような障害が発 生したことが考えられるか。2つ以上示しなさい。

★問題(503) RPCにおける障害回復

問題(502) で示した障害のうち、1つを選んで回復 する方法を示しなさい。
Last updated: 2010/01/07 16:50:40
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>