分散システムとは

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

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

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

■連絡事項

■今日の大事な話

■分散システム以前、分散システムではないコンピュータ

◆単体のコンピュータ(集中システム)

メインフレーム、大型計算機
ミニコンピュータ
ワークステーション
PC (Personal Computer)

◆メインフレームと端末

メインフレーム1台、端末n台

図? メインフレームと端末

利用者は地理的に分散(distributed)しているが、プログラムは、すべて中央の1台のコン ピュータで動作する。

文字端末の役目

「端末」は、メインフレーム(ホスト)と文字回線で接続されている。 電話回線等を使ったモデムによる接続。2400bps-9600bps。 IBMのものは、半二重。

◆単体のコンピュータの性質

CPU、メモリ、I/O、OS、アプリケーション

図? 集中型システムのハードウェアとOS

ハードウェアやオペレーティング・システムの働きで、次のようなことが(あ る程度のレベルで簡単に)実現できる。 分散システムでは、どれも難しい。

◆ファイルの内容の一貫性

Process1()
{
  write(file1,buf,bytes);
  ...
  ...
}
Process2()
{
  ...
  ...
  read(file1,buf,bytes);
}
Process3()
{
  ...
  ...
  read(file1,buf,bytes);
}
あるプロセスがwrite() した結果は、他のプロセスがread() したら即座に読める。

■分散システムを動作させるためのハードウェア

分散システムは、複数のコンピュータ(CPUとメモリ)を使う。 CPU とメモリの接続の仕方には、さまざまな種類がある。

◆共有メモリ型マルチプロセッサ(shared memory multiprocessor)

(CPU+Cache)*n+メモリ

図? 共有メモリ型マルチプロセッサ(バス共有)

◆LANに接続されたコンピュータ群

PC*3--hub

図? LANに接続されたPC

遠隔のメモリは、アクセスできない(no remote memory access)。 機種が違うこともある。

◆ネットワーク

◆ネットワーク速度の感覚

交通機関ネットワーク
徒歩 4km/h64kbps 電話回線
自動車 40km/h640kbps ADSL(低速)
飛行機 800km/hイーサネット(10Mbps), ADSL(中速), 無線LAN
人工衛星(第一宇宙速度) 28440km/h450Mbps, イーサネット(100Mbps-10Gbps)

■分散システムの目標

分散システムとは

最後の性質が大事である。

仮想的(virtual)
事実上の。実と同じように使える。
実体が「ない」ということが主ではない。 「仮想」は「透明性(透過性、transparency)」と 関係が深い。

◆分散システムのアプリケーション

◆分散システムの利点

◆分散システムの弱点

単体のコンピュータのハードウェア では簡単だったことが、分散システムでは困難になる。

◆分散システムの設計目標

分散は、「離れていても心は1つ」を目指す。 そのためには、集中より遅くなっても許されることがある。 並列処理は、単体のシステムより遅いことは許されない。

◆スケーラビリティ

構成要素の数が増えた時にどうなるか。 10台で動くものが100台、1000台で動くか。

◆分散システムを実現するための技術

◆トレードオフ

完全を目指そうとすると、急激にコストが高くなる。

実用になり、かつ、利益に見合うコストで実現できる範囲を探すことが大事になる。

◆負荷分散

負荷分散(load sharing, load balancing)の分散と分散システム (distributed systems)の分散は、意味が違う。

■集中アプリケーションの例

ゲームのハイスコア(得点の高い人n人の得点と名前)の維持。

ゲーム本体、ライブラリ、OS、ハードディスク、ファイル

図? 単一のコンピュータでハイスコアをファイルに保存

ライブラリの機能
int get_highscore(score_record_t records[], int len)
現在のハイスコア(score_record_t型)を最大 len 個だけ取得する。 実際に保存していた数を返す。
int put_score(int score, char *user)
新たにスコアを追加する。現在のものよりも得点が高ければ追加する。 引数は、得点とユーザ名。
これを、集中システム(1台のコンピュータ)で動作させたい。 (以下のプログラムは、分散システムのプログラムではない。) 分散システムでは、困難なことも集中システムでは簡単になっている 点に着目すること。

◆実行例

例題をコピーするには、cp コマンドを使うとよい。最後のディレクトリ名 「.」を忘れないように。
% mkdir hiscore-c [←]
% cd hiscore-c [←]
% cp ~yas/dsys/highscore/centralized/highscore.h . [←]
% cp ~yas/dsys/highscore/centralized/highscore.c . [←]
% cp ~yas/dsys/highscore/centralized/add-hiscore.c . [←]
% cp ~yas/dsys/highscore/centralized/show-hiscore.c . [←]
% cp ~yas/dsys/highscore/centralized/Makefile . [←]
% ls [←]
Makefile	add-hiscore.c	highscore.c	highscore.h	show-hiscore.c
% make [←]
cc -g   -c -o add-hiscore.o add-hiscore.c
cc -g   -c -o highscore.o highscore.c
cc -g add-hiscore.o highscore.o -o add-hiscore
cc -g   -c -o show-hiscore.o show-hiscore.c
cc -g show-hiscore.o highscore.o -o show-hiscore
% ls [←]
Makefile	add-hiscore.c	highscore.c	highscore.o	show-hiscore.c
add-hiscore	add-hiscore.o	highscore.h	show-hiscore	show-hiscore.o
% []
make コマンドを実行すると、Makefile の記述に従い2つの実行 形式のファイル add-hiscoreadd-hiscore が作られる。

例題を実行するには、環境変数 hiscoredata にファイル名を 設定する。その方法は、make help で表示される。

% make help [←]
setenv hiscoredata `mktemp /tmp/coins-dsys-XXXXXXXXXX`
ls -l $hiscoredata

./add-hiscore score name
./show-hiscore num

rm $hiscoredata
% []
help に従い、/tmp の下に予測が困難なファイル名でデータファイルを 作成する。(ホーム・ディスクの下に作成した場合は、うまく動作しない。理由 は後述。)

mktemp コマンドを実行し、その出力で、 環境変数 hiscoredataを設定する。

% setenv hiscoredata `mktemp /tmp/coins-dsys-XXXXXXXXXX` [←]
% printenv hiscoredata [←]
/tmp/coins-dsys-8ofae8kMu4
% echo $hiscoredata [←]
/tmp/coins-dsys-8ofae8kMu4
% ls -l $hiscoredata [←]
-rw-------   1 yas  wheel  0 Nov 30 20:10 /tmp/coins-dsys-8ofae8kMu4
% []
この例では、mktemp コマンドは、 /tmp/coins-dsys-8ofae8kMu4 というファイルを 作成し、そのファイル名を標準出力に出力する。 実験が終了したら rm コマンドで削除すること。

バッククォート(` `は、シェルの文法で、 コマンドの出力をコマンド行に持ってくる。上の例では、以下のコマンドと同 じである。

% setenv hiscoredata /tmp/coins-dsys-8ofae8kMu4 [←]
環境変数は、csh の組み込みコマンド printenv で表示できる。 シェルでは、$ をつけて参照することもできる。 プログラムの中では、getenv() ライブラリ関数で参照できる。

環境変数 hiscoredata を設定した後は、add-hiscore コマンド と show-hiscore を実行することができる。

% ./add-hiscore 10 "Yasushi Shinjo" [←]
% ls -l $hiscoredata [←]
-rw-------   1 yas  wheel  32 Nov 30 20:11 /tmp/coins-dsys-8ofae8kMu4
% ./show-hiscore 10 [←]
        10 Yasushi Shinjo
% ./add-hiscore 20 "Kazuhiko Kato" [←]
% ./show-hiscore 10 [←]
        20 Kazuhiko Kato
        10 Yasushi Shinjo
% ls -l $hiscoredata [←]
-rw-------   1 yas  wheel  64 Nov 30 20:24 /tmp/coins-dsys-8ofae8kMu4
% []
実験が終了したら、rm コマンドで削除する。
% ls -l $hiscoredata [←]
-rw-------   1 yas  wheel  64 Nov 30 20:24 /tmp/coins-dsys-8ofae8kMu4
% rm $hiscoredata [←]
% []

◆show-hiscore.c

get_highscore() 関数を呼び出す簡単なプログラム。 [show-hiscore.c]
   1:	
   2:	/*
   3:	        show-hiscore.c -- The main function for get_highscore().
   4:	        Created on: 2008/11/28 18:40:56
   5:	        ~yas/dsys/highscore/centralized/show-hiscore.c
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stderr, fprintf() */
   9:	#include <stdlib.h>     /* strtol() */
  10:	#include "highscore.h"
  11:	
  12:	static void show_score( int top );
  13:	
  14:	static void usage( char *comname ) {
  15:	        fprintf(stderr,"Usage: %% %s n\n", comname);
  16:	        exit( 1 );
  17:	}
  18:	
  19:	main( int argc, char *argv[], char *envp[] ) {
  20:	    int top;
  21:	    char *name ;
  22:	        if( argc != 2 )
  23:	            usage( argv[0] );
  24:	        top = strtol( argv[1], 0, 10);
  25:	        show_score( top );
  26:	}
  27:	
  28:	static void show_score( int top ) {
  29:	    score_record_t records[HIGHSCORE_MAX_RECORDS];
  30:	    int n, i ;
  31:	        if( top > HIGHSCORE_MAX_RECORDS ) {
  32:	            fprintf(stderr,"Warning: top too large: %d\n",top);
  33:	            top = HIGHSCORE_MAX_RECORDS;
  34:	        }
  35:	        n = get_highscore( records, top );
  36:	        for( i=0; i<n; i++ ) {
  37:	            printf("%10d %s\n", records[i].score, records[i].name );
  38:	        }
  39:	}

◆add-hiscore.c

put_score() 関数を呼び出す簡単なプログラム。 [add-hiscore.c]
   1:	
   2:	/*
   3:	        add-hiscore.c -- The main function of put_score().
   4:	        Created on: 2008/11/28 17:22:21
   5:	        ~yas/dsys/highscore/centralized/add-hiscore.c
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stderr, fprintf() */
   9:	#include <stdlib.h>     /* strtol() */
  10:	#include "highscore.h"
  11:	
  12:	void usage( char *comname ) {
  13:	        fprintf(stderr,"Usage: %% %s score \"User Name\"\n", comname);
  14:	        exit( 1 );
  15:	}
  16:	
  17:	main( int argc, char *argv[], char *envp[] ) {
  18:	    int score ;
  19:	    char *name ;
  20:	        if( argc != 3 )
  21:	            usage( argv[0] );
  22:	        score = strtol( argv[1], 0, 10);
  23:	        name  = argv[2];
  24:	        put_score( score, name );
  25:	}

◆データ構造とインタフェース

[highscore.h]
   1:	
   2:	/*
   3:	        highscore.h -- Hiscore Library for centralized systems.
   4:	        Created on: 2008/11/28 11:49:18
   5:	        ~yas/dsys/highscore/centralized/highscore.h
   6:	*/
   7:	
   8:	#ifndef _HIGHSCORE_LIB_CENTRALIZED_H_
   9:	#define _HIGHSCORE_LIB_CENTRALIZED_H_
  10:	
  11:	#define HIGHSCORE_MAX_RECORDS   10
  12:	#define HIGHSCORE_NAME_LEN      28
  13:	#define HIGHSCORE_FILE_ENV      "hiscoredata"
  14:	
  15:	struct score_record {
  16:	        int      score;
  17:	        char     name[HIGHSCORE_NAME_LEN];
  18:	};
  19:	typedef struct score_record score_record_t;
  20:	
  21:	extern int get_highscore(score_record_t records[], int len);
  22:	extern put_score(int score, char *user);
  23:	
  24:	#endif  _HIGHSCORE_LIB_CENTRALIZED_H_

◆ライブラリ

[highscore.c]
   1:	
   2:	/*
   3:	        highscore-lib-centralized.c -- Hiscore Library for centralized systems.
   4:	        Created on: 2008/11/28 11:45:58
   5:	        ~yas/dsys/highscore/centralized/highscore-lib-centralized.c
   6:	*/
   7:	
   8:	#include <fcntl.h>      /* open() */
   9:	#include <sys/types.h>  /* fstat() */
  10:	#include <sys/stat.h>   /* fstat() */
  11:	#include <string.h>     /* memcpy(), memset() */
  12:	#include <stdio.h>      /* snprintf(), perror() */
  13:	#include <stdlib.h>     /* getenv() */
  14:	#include <sys/file.h>
  15:	
  16:	#include "highscore.h"
  17:	
  18:	static int insert_score( score_record_t records[], int len, int nelement,
  19:	                         int score, char *user );
  20:	static int find_posision( score_record_t records[], int len, int nelement,
  21:	                          int score );
  22:	

◆get_highscore()

  23:	int get_highscore(score_record_t records[], int len) {
  24:	    int fd;
  25:	    struct stat stat;
  26:	    ssize_t bytes;
  27:	    char *filename;
  28:	
  29:	        if( (filename=getenv(HIGHSCORE_FILE_ENV)) == NULL ) {
  30:	            fprintf(stdout,"No environment: %s\n",HIGHSCORE_FILE_ENV );
  31:	            return( 0 );
  32:	        }
  33:	        if( (fd = open(filename,O_RDONLY)) < 0 ) {
  34:	            perror( filename );
  35:	            return( 0 );
  36:	        }
  37:	        if( flock(fd,LOCK_SH) < 0 ) {
  38:	            perror("flock");
  39:	            goto error0;
  40:	        }
  41:	        if( fstat(fd,&stat) < 0 ) {
  42:	            perror("fstat");
  43:	            goto error1;
  44:	        }
  45:	        bytes = (stat.st_size <= len*sizeof(score_record_t)) ?
  46:	                 stat.st_size :  len*sizeof(score_record_t) ;
  47:	        if( read(fd, records, bytes)!=bytes ) {
  48:	            perror("read");
  49:	            goto error1;
  50:	        }
  51:	        if( flock(fd,LOCK_UN) < 0 ) {
  52:	            perror("flock");
  53:	            goto error0;
  54:	        }
  55:	        close( fd );
  56:	        return( bytes/sizeof(score_record_t) );
  57:	
  58:	error1: flock( fd,LOCK_UN );
  59:	error0: close( fd );
  60:	        return( 0 );
  61:	}
  62:	

◆読み書きロック

flock() システム・コールは、BSD系Unixにあるシステム・コールで、 読み書きロックを実現する。
LOCK_SH (shared lock または read lock)
読み込むだけのプロセスならば、複数同時に実行できる。 (読むだけでもロックする必要がある。さもないと、読んでいる途中で変化することがある。 )
LOCK_EX (exclusive lock または write lock)
書き込むプロセスは、1度に1つだけに制約する。

通常のロック、6スレッド、読み書きロック、Rか3つ、Wが2つ

図? 普通のロックと読み書きロック

注意: 最近では、flock() ではなくて、fcntl() を使うのが一般的。 fcntl() では、ファイルの一部をロックできる。

◆put_score()

  63:	int put_score(int score, char *user) {
  64:	    int fd, n;
  65:	    struct stat stat;
  66:	    ssize_t bytes;
  67:	    off_t offset ;
  68:	    score_record_t records[HIGHSCORE_MAX_RECORDS];
  69:	    char *filename;
  70:	
  71:	        if( (filename=getenv(HIGHSCORE_FILE_ENV)) == NULL ) {
  72:	            fprintf(stdout,"No environment: %s\n",HIGHSCORE_FILE_ENV );
  73:	            return( 0 );
  74:	        }
  75:	        if( (fd = open( filename,O_RDWR|O_CREAT, 0666)) < 0 ) {
  76:	            perror( filename );
  77:	            return( 0 );
  78:	        }
  79:	        if( flock(fd,LOCK_EX) < 0 ) {
  80:	            perror("flock");
  81:	            goto error0;
  82:	        }
  83:	        if( fstat(fd,&stat) < 0 ) {
  84:	            perror("fstat");
  85:	            goto error1;
  86:	        }
  87:	        bytes = (stat.st_size <= HIGHSCORE_MAX_RECORDS*sizeof(score_record_t))?
  88:	                 stat.st_size  : HIGHSCORE_MAX_RECORDS*sizeof(score_record_t) ;
  89:	        if( read(fd, records, bytes)!=bytes ) {
  90:	            perror("read");
  91:	            goto error1;
  92:	        }
  93:	
  94:	        n = insert_score( records,HIGHSCORE_MAX_RECORDS,bytes/sizeof(score_record_t),
  95:	                          score, user );
  96:	
  97:	        if( n > 0 ) {
  98:	            offset = 0L;
  99:	            if( lseek( fd, offset, SEEK_SET )< 0 ) {
 100:	                perror("lseek");
 101:	                goto error1;
 102:	            }
 103:	            bytes = n * sizeof(score_record_t);
 104:	            if( write(fd, records, bytes)!=bytes ) {
 105:	                perror("write");
 106:	                goto error1;
 107:	            }
 108:	        }
 109:	        
 110:	        if( flock(fd,LOCK_UN) < 0 )  {
 111:	            perror("flock");
 112:	            goto error1;
 113:	        }
 114:	        close( fd );
 115:	        return( 1 );
 116:	
 117:	error1: flock( fd,LOCK_UN );
 118:	error0: close( fd );
 119:	        return( 0 );
 120:	}
 121:	

◆その他

 122:	static int insert_score( score_record_t records[], int len, int nelement,
 123:	                         int score, char *user ) {
...
 140:	static int find_posision( score_record_t records[], int len, int nelement,
 141:	                          int score ) {
...
内容は、データ構造とアルゴリズムの範囲であり、分散システムでは省略する。

[Makefile]

■分散アプリケーションの作り方

集中システム用に記述したアプリケーション・プログラムを、共有メモリがな い複数のコンピュータで動作させる方法。

◆ネットワーク・オペレーティング・システム(network operating system)

ネットワーク通信の機能を提供したオペレーティング・システム。 ネットワーク・オペレーティング・システムでは、利用者は、遠隔のコンピュー タを使うためには、明示的にコンピュータを示す必要がある。

注意:r系コマンドは、ネットワーク上を生パスワードが流れて危険なので、使われない。 その代わりに、ssh, slogin, scp が使われる。

現在、広く使われているオペレーティング・システムで、 ネットワーク通信機能があるものは、これに分類される。

◆ネットワーク・オペレーティング・システムでの分散アプリケーションの実行

ネットワークOS、ゲーム本体、データ保持プロセス、通信

図? ネットワークOSでのアプリケーションの実行

◆分散型オペレーティング・システム

ネットワーク・オペレーティング・システムが発展したもの。

目標

分散OSの種類

実装は困難。Microsoft でも Sun Microsystems でも作れない。

◆分散型オペレーティング・システムでの分散アプリケーションの実行

分散OSは1つ、アプリケーションは、集中用ものがそのまま動作

図? 分散OSでのアプリケーションの実行

分散アプリケーションを分散オペレーティング・システムで走せると、やや矛 盾がある。

◆現状の分散オペレーティング・システム的な機能

ファイル・サービス NFS (Network File System), SMB/CIFS
遠隔にあるファイルとローカル・ファイルを同じように扱える。
名前サービス NIS (Network Information System), LDAP (Lightweight Directory Access Protocol)
パスワード・ファイルを複数のコンピュータで共有できる。 一カ所で変更すると全てのコンピュータで変更される。

◆ミドルウェア

オペレーティング・システムとアプリケーションの間に入り、 (主に通信に関する)サービスを提供する。 下位層のオペレーティング・システムとは独立している。

種類

◆ミドルウェアを用いた分散アプリケーションの実行

OSは複数、ミドルウェアは1つ、アプリケーションは、通信なし

図? ミドルウェアを用いた分散アプリケーションの実行

◆分散型プログラミング言語

集中システム用のプログラムと同じように書いたプログラムが、自動的に分散 アプリケーションになる。言語処理系が、ネットワークの存在や通信をさまざ まな度合いで隠す。

どこまで隠せば分散型プログラミング言語と呼んでもよいか? 「socket があれば、分散型プログラミング言語」とは、定義したくない。 永続言語との比較。


Last updated: 2008/12/14 15:00:28
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>