select - みる会図書館


検索対象: UNIX MAGAZINE 2002年1月号
13件見つかりました。

1. UNIX MAGAZINE 2002年1月号

ますは関数の引数です。次のようにたいへん多くの引数 をとります ( 誌面の都合長で折り返しています。以下 int process int netin —rings (netin, netout , netex, ttyin, = 〉 ttyout, P011 ) / * If 0 , then block P011 ; until something tO d0 * / 、 ttyin などの引数には、リングバッフアに空き プログラミング・テクニック ここでは最初に netin や netout などの値を初期化し ていませんが、この関数の実行か開始される点では、 れらはつねに空の状態になっています。これらは外部変数 であり、一度ネ加月化されると以降は初期化せずに使い続け ます。この段階では集合に要素が j 助日された状態になって いますが、後ろの部分の処理カ鮗ったときには FD-CLR を使ってかならす要素がない状態に戻しています。 次が select システムコールです。引数のところでも説 明したように、 p 。Ⅱ引数が真の場合にはさきほど定義した TimeValue をタイムアウトとして指定し、ポーリンク鋤 作となるようにしています。 があれは真の値カイに入されています。つまり、バッフアに オ褓内できるスペースがあるので、 netin ならネットワーク から、 tty ⅲなら標準入力から言囚みをおこなってもよい という意味です。一方、 netout や ttyout はリングバッ フアにデータがあるかどうかを表します。つまり、 netout ならネットワークへの、 ttyout なら標準出力への書込み をおこなってもよいという意味になります。 po Ⅱ引数は、 コメントにもあるように、値カイ為なら select をプロック します。 select 関数のための引数としては、以下のものを定義し ています。とくに注目してはしいのが TimeValue 変数 です。 register int C ; int returnVa1ue = 0 ; static struct timeval TimeVa1ue = この変数は、あとで select のタイムアウト引数として 利用します。タイムアウト値として 0 秒を指定しているた め、実際の重川はポーリングになります。 次に、 FD-SET を使って select に指定する集合を設定 します。このとき、引数の値にもとづいてファイル・ディ スクリプタを追加していきます。 if ((c if seIect(16, &ibits , ( P011 (struct timeval * ) 0 (c &obits, &xbits, &TimeVa1ue) ) く 0 ) { (netout) { FD_SET (net , if (ttyout) { FD_SET (tout , if (ttyin) { FD_SET(tin , if (netin) { FD_SET (net , (netex) { FD_SET (net , if if &obits) ; &obits); &ibits) ; &ibits) ; &xbits) ; UNIX MAGAZIN E 2002.1 if (errno return 0 ; printf ("s1eep(5) from telnet , - after select : %s\r\n" , strerror(errno) ) ; s1eep(5) ; = EINTR) { return 0 ; クリプタです。これはネットワークからの言もムみ用のファ になります。ますは、例外条負 : を表すファイル・ディス 以降は、各ファイル・ディスクリプタに関連した処理 間を作ったあとに return で関数を終了しています。 対処方法があるわけではないので、 sleep ですこし待ち時 を石忍しています。とはいえ、それ以外の場合にもとくに 返ると同時に errno に EINTR カ材褓内されるので、それ ます。割込みにより select の実行カ鮗了した場合、一 1 が こではその処理もしてい が失敗したようにみえるので、 られない状況もありえます。そのときも見かけ上は select ている場合には、割込みなどにより select の実行を続け ラーが発生したときです。しかし、 select がプロックし select システムコールが一 1 を返すのは、基本的には工 うがよいように思えます。 ことも考えられます。もうすこし汎用的な手段をとったほ のでしようが、別の部分を修正した場合は 16 以 E になる ディスクリプタは 16 を超えることはないと石言している こで扱うファイル・ おそらく telnet コマンドの作者は、 select の第 1 引数として整数の 16 を指定しています。 81

2. UNIX MAGAZINE 2002年1月号

s ele ct の利用 した 3 つの集合に、みつかったファイル・ディスクリプ には、ファイル・ディスクリプタの甫を渡すために利用 該当するファイル・ディスクリプタがみつかったとき おいてエラーが発生したことを示します。 select システムコールが負の値を返した場合は、呼出しに か満たされる前にタイムアウトとなったことを表します。 れは、ファイル・ディスクリプタに対して指定した条件 トを設定すると、この値が 0 になることがあります。 常は、これは正の直になっているはすです。タイムアウ を満たしたファイル・ディスクリプタの数を表します。通 select システムコールカ鮗了したときの戻り値は、条件 ル・ディスクリプタがあるかどうかを即座に調べられます。 せん。時間として 0 を指定すれば、条件を満たすファイ ステムコールは最大でも指定された時間しかプロックしま 方、 timeval 構造体を使って時間を指定すると、 select シ する条件が岡たされるまで、そこで処理は止まります。 ます。つまり、いすれかのファイル・ディスクリプタに関 NULL を指定すると select システムコールはプロックし すファイル・ディスクリプタがない場合、最後の引数に テムコールはプロックせすにすぐに戻ります。牛を満た ここ旨定した値に関係なく select シス プタがあれは、 指定するわけです。条件を満たすファイル・ディスクリ を満たすファイル・ディスクリプタがない場合の動作を イル・ディスクリプタを検査しますが、指定された条件 テムコールでは言ムみ、書込み、帯域外データを扱うファ するか、タイムアウトするかを指定します。 select シス 最後の引数の timeout には、 select の処理をプロック なります。 るかを指定します。さきほどの例でいえば、 fdmax 十 1 と 値十 1 、つまりいくつのファイル・ディスクリプタを調べ nfds には、検査するファイル・ディスクリプタの最大 select(nfds, &rfds, &wfds, &efds, &timeout) 定します。 efds とすると、 select システムコールは以下の形式てオ日 したかどうかを調べるファイル・ディスクリプタの集合を ル・ディスクリプタの集合を wfds 、帯域外データか到着 タの集合を rfds 、書込みが可能かどうかを調べるファイ 言ムみか可能かどうかをヾるファイル・ディスクリプ 80 タかオ褓内されています。そのため、集合に含まれるすべて のファイル・ディスクリプタに対する処理としてさきほど 紹介したループを使って、集合に残っているディスクリプ タを調 , 、く、それを処理します。 図 1 のプログラムでは、標準入力からの入力とネット ワークからの入力を select システムコールで監視してい ます。このプログラムでは入力がなければはかに処理する ことはないので、 select システムコールは入力が到着する までプロックするようになっています。 select システムコ ーノレから戻ってきた場合には、 FD-ISSET を使って標準 入力やネットワークからの読込みが可能かどうかを調べ、 入力があればそこからデータを読み込みます。そして、標 準入力ならネットワークへネットワークからの入力な ら標準出力へ書き出します。ほかにも、入力カ鮗了したと きにプログラムを終了できるように残っているファイル・ ディスクリプタの数を数えたり、 alarm を使ってタイム アウトを設定し、プログラムを終了できるようにしたりし ています。 図 1 のプログラムでは、標準入力とネットワークから の入力の 2 つを監視していました。一方、ネットワークや 標準出力への出力についてはとくに見をおこなわす、い きなり出力しています。ネットワークの先に書き込めない 場合、このプログラムでは書込みの段階でプロックしてし まいます。本来なら、入力を読み込んでもそれをすぐに書 き込むのではなく、書込みが可能かどうかを調べてから書 き込むべきです。この場合には、読み込んだデータを一引芋 的にどこかにイ尉寺しておかなければなりません。入力が可 能になったので読み込んだが、まだ出力できない状態だっ たということもあるためです。しつは、 telnet コマンドで この情報を保持するために利用されるのが、前回紹介した リングバッフアです。 それでは、 telnet コマンドでこの処理をおこなっている 部分をみていきましよう。具イ勺には、 sys-bsd ファイル にある process-rings 関数です。この関数は TN3270 マ クロが定義されているかどうかによる条件コンパイルが多 く、見通しか懇くなっているので、関係のない部分を削除 した状態でみることにしましよう。 出力先の監視 UNIX MAGAZINE 2002.1

3. UNIX MAGAZINE 2002年1月号

プログラミング・テクニック 図 1 変更した process 関数 sig—alrm(int) ; void VOid process(int fd) char int buf [BUFSIZ] ; len; fd—set fds, ofds; int count = 2 ; FD_ZERO (&ofds) ; FD_SET(), &ofds) ; FD_SET(fd, &ofds) ; do { FD_COPY(tofds , &fds) ; if (select(fd + 1 , &fds , NULL, perror("select") ; exit(l); if (FD-ISSET(O, &fds)) { if ((len = read(), buf, perror ( "read" ) ・ exit(l); if (len NULL , NULL) く 0 ) { BUFSIZ)) く 0 ) { FD_CLR(), &ofds) ; count— shutdown()d , SHUT—WR) ; a1arm(3) ; signal (SIGALRM , sig-alrm) ; } else if (write(fd, buf , len) ! = len) break; if (FD_ISSET(fd, &fds) ) { read(fd, buf, BUFSIZ)) く 0 ) { if ((len if (len } else if (write(l , buf, len) ! = len) break; count— FD_CLR(fd, &ofds) ; exit(l) ; } while(count) ; void sig-alrm(int sig) exit(O) ; です。たとえば fdmax などの変数に、登録したファイル・ ディスクリプタの最大値をオ内します。こうすれは、 0 ~ fdmax までのファイル・ディスクリプタを詩ヾれは・よい と分かります。なぜなら、 select がおこなう操作では、最 初にユーサーカ甘旨定した集合の要素 ( ファイル・ディスク リプタ ) のみが対象となるためです。登録したファイル・ ディス久リプタの最大値として fdmax を閑寺していれば、 UNIX MAGAZINE 2002.1 次のようなループて集合に登録されているすべての要素に 関する処理がおこなえます。 for ()d = 0 ; fd く = fdmax; fd + + ) { if ( !FD_ISSET(fd, &fds)) continue; 79 こで fd を用いた処理をおこなう * /

4. UNIX MAGAZINE 2002年1月号

exit(l); } else if (len break; len) ! = len) / * ネットワークから入力 * / } else { if if (write(fd, buf , / * ネットワークに出力 * / exit(l); perror ( "read") ; (errno ! = EAGAIN) { if ((len = read(fd, buf , BUFSIZ)) write(), buf, / * 端末への出力 * / } else { break ; } else if (len len) ; se 厄 ct システムコール く 0 ) { 上記のように修正したプログラムの入出力を処理するル ープに注目すると、 for の無限ルーフ。のなかで、非プロッ キング入出力の 2 つのファイルの入力を試み続けるビジー ウェイトと呼はれるループとなっています。標準入力と ネットワークのいすれからも入力するデータがない場合、 読込みを試みて EAGAIN を得たあと、さらに言もムみを 試みて EAGAIN を得るという処理を繰り返すことにな ります。これでは CPU 資源の無驪貴いといわれても仕方 がありません。 入出力のデータがあるときだけ処理したい場合は、 se- lect システムコールを利用するのかイ甦リです。たとえは、 サンフ。ル・プログラムの pr 。 cess 関数を図 1 のように変更 すれは、端末からの入力とネットワークからの入力をどの 順番でもおこなえるようになります。しかも、ビジーウェ イトも起こりません。 急にプログラムか難しくなったように感しるかもしれま せんが、 select システムコールの動作を理解す川まさほど 難しくはありません。それでは、 select システムコールに ついて詳しくみていきましよう。 fd-set 本体 select システムコールは、ファイル・ディスクリプタの 集合に対して言も囚みや書込みが可能かどうかを調べること 78 かできます。さらに、帯域外データと呼ばれる朱なデー タが到着したかどうかも調べられます。ファイル・ディ スクリプタの集合を表現するために、 fd_set という専用の データ構造が用意されています。このデータ構造に対する 演算としては、以下のものカ甘是供されています (fd-set 型 の変数 fds か旦言されているとします ) 。 FD ZERO(&fds) : ファイル・ディスクリプタの集合 を空集合にネノ期化する。 UNIX MAGAZINE 2002.1 合に要素を追加するときにその最大値 &f 尉寺しておくガ去 大値を得るガ去があります。それは、 FD-SET により集 しかし、もっと簡単に集合の要素となる可能性のある最 には、 sysctl 関数を利用して値を取得することになるで 変数として取得できます。プログラム中で正しい値を使う す。これは sysctl コマンドでも kern. maxfilesperproc 許されているファイル・ディスクリプタの最大値になりま limit コマンドで得られる descriptors のイ直かフ。ロセスに 過度に大きく見積もられた値となっています。実際には、 のファイル・ディスクリプタを扱えます。ただしこれは、 用している値です。 fd-set 型は、この値で示される数まで もっとも簡単です。これは fd-set 型を定義するときに利 ファイルで定義されている FD-SETSIZE を用いるのが 値です。この値には、 /usr/include/sys/types. h ヘッダ 素となる可能生のある値とはファイル・ディスクリプタの FD-ISSET を使って検査することになります。集合の要 いません。要素となる可能性のあるすべての値について、 どの操作がほしいところですが、残念ながら用意されて このほかに、集合に属するすべての要素を処理するな す。 構造体として宣言されているため、直接代入できるからで には、このマクロはあまり利用されません。 fd-set 型が んが FD-COPY(&src, &dst) カイ吏えます。しかし実際 合全体をコピーするには、マニュアルには書いてありませ を使って集合に含まれる要素を順に追加していきます。集 通常は、ます FD-ZERO て集合を祺月化し、 FD-SET かを検査する。 FD-ISSET(fd, &fds) : 集合に fd が含まれるかどう FD-CLR(fd, &fds) : 集合から fd を削除する。 FD-SET(fd, &fds) : 集合に fd を追加する。

5. UNIX MAGAZINE 2002年1月号

I/O デバイスの仮 図 4 仮想マシン・ネットワークでのバケットの送言 ネットワーク・バケットの送信 ゲスト OS ↓ レ 0 ポートへの OUT VMM コンテキストの切替え VMDriver ↓ VMApp に戻る VMApp ↓ Syscall VMNet ドライバ ↓ ブリッジコード ホスト Ethernet ドライバ ↓ レ 0 ポートへの OUT Ethernet ハードウェア / ヾケットの送信 ネットワーク・バケットの受信 Ethernet/ 、一ドウェア ↓ デバイスの割込み ホスト Ethernet ドライバ ↓ ブリッジコード VMNet ドライバ ↓ select() から戻る VMApp VM メモリへの memcpy VMM に旧 Q の問合せ VMM ↓ 旧 Q を上げる ゲスト OS ↓ レ 0 ポートへの爪 / OUT VMM ↓ コンテキストの切替え VMDriver ↓ IOCTL から戻る VMApp バケットの受信完了 のテンヾイスドライバに伝えるために VMM がイ反想 IRQ ものと同しである。 VMware Workstation の一瓦想 NIC を発行する。 は AMD Lance Am79C970A [ 1 ] コントローラをもとに しているが、特定のネットワーク速度には制限されない。 バケットの受信は逆の順序で処理される。プリッジ先の ホスト NIC はバケットを VMNet に送信する。 VMApp は VMNet ドライバに対して select() を定期的に実行す る。もし読み取るべきバケットが届いているなら read() 前述のホスト協調型のイ反想 NIC 工ミュレーションを通 でバケットを読み取って、 VMM にイ課 IRQ の発行を したバケットの送受信にかかわる構成要素を図 4 に示す。 依頼する。 VMM はイ反想 IRQ を発行し、ゲスト OS の ゲスト OS は、 Lance コントローラ用のテンヾイスドライバ Lance ドライバが - ー里の I/O アクセスを発行して、バケ を実行する。ドライバは、一漣の仮想 I/O ポートの言翹 ( ットの受信をハードウェアに通知する。 りと書込みをおこなってバケットの送信を開始する。それ 図 4 において点線で囲まれた部分は、バケットを実際 ぞれの I/O ポートへのアクセスは、 Lance ポートへのア に送受信するポートアクセスのイ課化によって、よぶんな クセスをエミュレートするための V App への切替えを ワークロードが発生することを示している。さらに、イ瓦想 惹き起こす。最後の OUT 命令の際、 Lance 工ミュレー IRQ の処理に起因する I/O アクセスや特権命令の処理も ションは VMNet ドライバに通常の write() を発行する。 必要になる。このうち、イ瓦想 Lance のアドレスレジスタへ これにより、バケットがホスト NIC を介してネットワー のアクセスは完全に VMM の内部て処理されるが、デー クに送信される。さらに、 VMApp から VMM への切替 タレジスタへのアクセスはすべて VMApp に切り替えて ーえがおこなわれ、バケットが送信されたことをゲスト OS 点線で囲まれた部分は、ホスト協調型アー キテクチャでネットワーク・デバイスを仮 想化する際に特有の構成要素を表す。 23 仮想 NIC による送受信 163 UNIX MAGAZINE 2002.1

6. UNIX MAGAZINE 2002年1月号

イル・ディスクリプタとなっています。まず、 FD-CLR で xbits から net を削除しています。この処理により、 xbits は要素がない状態に戻ります。これが、この関数の 冒頭で xbits などをネ川月化せずにすんた理由です。 if (FD_ISSET(net, &xbits)) { FD_CLR(net , &xbits) ; SYNCHing = 1 ; (void) ttyflush(l) ; / * flush already enqueued data * / ネットワークからの言ムみにおける例外条件とは、帯域 外データの至睹を表します。 telnet コマンドでは、割込み 文字を送信するときに帯域外データとして送出します。帯 域外データは通常のデータストリームとは別に到着するた め、割込み文字だけを早く送信するといったことか可能に なります。 例外条件として指定していた net ソケットは、通常の 入力としても検査しています。こちらは帯域外データでは ない通常のデータストリームにデータが到着しているかど うかを詩・ヾるものです。ここでは、ネットワークから読み 込むデータをリングバッフアにオ褓内するために、ますリン グバッフアの空き領域の大きさを取得しています。 if (FD_ISSET(net, &ibits)) { どうかによる条件コンバイル部分カきます。定義されて さらに、 SO ー OOBINLINE マクロか定義されているか / * !defined(SO—OOBINLINE) * / netiring ・ supply , canread, 0 ) ; C recv (net , (char * ) / * !defined(SO-OOBINLINE) * / !defined(SO_OOBINLINE) (&netiring) ; canread = ring_empty_consecutive FD_CLR(net , &ibits) ; int #else #if #endif ないことを石忍し、 netiring の、、針 " を進めて言ムみを終 このあと、 c の値を用いて言もムみでエラーが発生してい データを読み込むイ乍業になります。 るように、さきほど取得した空き領域の大きさまて涌常の のマクロが定義さオした状態になっているので、すぐ下にあ ための処理をしています。ただし、 FreeBSD などではこ にそれを正しく通常のデータストリームのなかに配置する いない場合の部分は、複数の帯域外データが到着したとき 82 了します。 if (c く 0 & & errno = = EWOULDBLOCK) { c = 0 ; } else if (c く = 0 ) { return ー 1 ; UNIX MAGAZINE 2002 ユ ( たしみ・ひさかす ) たでしようか。 て非垬月入出力をおこなっていることか理解していただけ ンドが、入出力でプロックしないように紐じ、の注意を払っ れ netoring と ttyoring に移動しています。 telnet コマ netiring にオ褓内された入力を適当に解釈しながら、それぞ データをネットワークに出力します。さらに、 ttyiring と オ褓内されたデータを標準出力に、 netoring に格納された 書込みが可能になったことを石忍しながら、 ttyoring に トワークからの入力を netiring に格納します。そして、 を石忍しながら、標準入力からの入力を ttyiring に、ネッ telnet コマンドでは、 select でデータか準備できたこと を説明しました。 こなうための冓として、 select システムコールの使い方 今回は、リングバッフアを使って非同期に入出力をお ☆ おこない、この関数は終了します。 さらに、標準出力への出力が可能になった場合の処理も returnVa1ue ー = netflush() ; FD_CLR(net , &obits) ; if (FD_ISSET(net, &obits)) { ているデータをネットワーク上に送出します。 は、 netflush 関数を呼び出すことで netoring にオ褓内され あります。ネットワークへの書込みか可能になった場合に 続いて、出力用のファイル・ディスクリプタの検査か はリングバッフアに読み込んで、、針 " を進める作業です。 よりもやや複雑になっていますが、さきはどと同様に基本 処理などが追加されているのでネットワークからの言もムみ 次は標準入力からの言ムみです。入力カ絲冬了した場合の returnVa1ue ring—supplied(&netiring, c) ; if (c) Dump( ' く ' , netiring ・ supply, c) ; if (netdata) {

7. UNIX MAGAZINE 2002年1月号

表 1 VMware 、 vorkstation 2.0 でのネットワーク送言 CPU の分布 ート 合言 カテゴリー VM M 日 VMNet 経由での送信 Lance ステータス・レジスタのエミュレ ホスト IRQ ( テパイス割込み ) の処理 Lance のエミュレート VMNet 経由での受信 VMM カテゴリー VMApp への切替えを要求する IN/OUT イ瓦想北を要求しない命令 イ瓦想北を要求する - ヨ殳命令 VMM て処理される IN/OUT Lance アドレスポートへの IN/OUT イ瓦想コードへ ( から ) の移行 IRET 命令のイ瓦想化 イ瓦想 IRQ ( テパイス割込み ) の伝土 俿冾 0.8 % 3.3 % 3.4 % 4.0 % 8.7 % 77.3 % 4.6 % 4.8 % 4.8 % 8.1 % 8.3 % 11.6 % 22.0 % 26.8 % 日の害冾 平均日繝 なし 3.93 s なし 0.74 卩 s 1.36 ″ s なし なし 7.45 ″ s 平均日鋿 1.8 s 5.2 s なし 3.1 / ↓ s 13.8 ″ s なし 最大のオーバーヘッドは、 VMApp , ) 鼬或切替えか要求される I/O 空間へ、のアク セスと、それを VMApp で処理する時間である。 くとも 2 倍は遅くなる引算である。 オーバーヘッドのおもな要因としては、このほかに表 1 中の複数のカテゴリーで必要な IRQ の処理か挙げられる。 AMD Lance の一反想 NIC は、 lntel EtherExpress の物 理 NIC と同様に、バケットの送受信のたびに IRQ ( テンヾ イス割込み ) を発行する。したがって、ネットワークの利 用率か高いワークロードではマシンの割込み率がきわめて 高くなる。ホスト協調型アーキテクチャでは、 VMM 領 域の実行中に受信される IRQ ごとに VMM の割込みハ ンドラか実行さホスト領域への切替えが発生する。ホ スト領域では、 IRQ に対応するホスト OS の割込みハン ドラを実行し、 VMApp に制御を渡してその結果の動作 が処理される。 IRQ がゲスト OS に関係するものである 場合 ( ゲスト宛のバケットが受信されたことを示すなど ) 、 VMApp はイ廨課 IRQ をゲスト OS に伝える必喫がある。 これには、 VMM 領域への切替え、イ瓦想マシンへの IRQ のイ幻土、ゲスト OS の割込みハンドラの実行がともなう。 VMM とホスト OS の割込みハンドラのはかにゲスト OS の割込みハンドラが実行されるため、 IRQ のコスト は増大する。また、イ反想割込み処理のルーチンは、イ廨当ヒ にコストがかかる牛罸雀モードの命令を実行する。表 1 を見 ると、 VMM が処理するはとんどの IN/OUT は仮想割 込みコントローラへのアクセスである。また、ほとんどの 166 IRET 命令はゲスト OS の割込みハンドラの最後で実行 される。そして、 VMM 領域でおこなわれる割込み処理 は、ホスト領域でおこなわれる割込み処理とくらべてはる かにコストか高い。これは、 VMM の割込みハンドラとホ スト領域への領域切替えか原因である。 ホスト協調型アーキテクチャには、この分析結果からは 分からないもう 1 つのオーバーヘッドがある。 VMApp と VMM は、イ廨課マシン用のイベントを生成するハ ウェア割込み ( ゲスト OS 宛のノヾケットが受信されるな ど ) と、仮想マシンとは関係のないハードウェア割込み を区別できないのである。これを区別できるのは、ホスト OS とそのドライバだけである。そこで、バランスをとる ための工夫をした。つまり、 VMApp は IRQ に応して、 VMM が VMApp に制御を戻したときに何もしないか、 あるいは VMApp から select() を呼び出す。 select() を 頻繁に呼び出しても無駄になるが、適度に呼び出さないと ネットワークの I/O イベントの処理リな遅れが生し る可能生がある。 3.3 ネットワークの仮想化による オーバーヘッドの軽減 領域切替えがオーバーヘッドの最大の要因だとした前節 の糸喆侖を踏まえ、ホスト協調型の I/O アーキテクチャを UNIX MAGAZINE 2002.1

8. UNIX MAGAZINE 2002年1月号

連載 UNIX Communication N0tes—O ために利用できるのが、 select() システムコールである。 select() は、複数のファイル識別子で指定される入力ソ ケットを監視し、カーネル内でプロックする。読み出せる ようになった ( 到着した ) ソケットがあるとプロックか外 れ、処理ルーチンを同一プログラム内でおこなうようにす るコードか書ける。これにより、複数のソケットを監視し ながら処理を夫行するルーチンか書けるようになった。 しかし、この実装には大きな問題がある。プログラムを 実行しているコンテキストは 1 つしかないので、リクエス ト処理ルーチンを実行しているあいだは、ほかのソケット に到着したデータや新たな接続要求はすべてカーネル内に イ尉寺される。けっきよく、 fork() は使わなくても、すべ ての処理がシリアライズされるため、高い性能か得にくく なった。もちろん、リクエスト処理ルーチンの処理量が大 きいと、処理待ちになるリクエストも増えることになって しまう。 そこで、登場するのがスレッドである。その考え方は 実装により多少異なるが、おおまかには次のようなもので ・スレッドを利用するプログラムでは、その実行時に複数 のスレッドを起動することができる。 ・ 1 つのスレッドは、実行を管理するためのコンテキスト をもつ。ここには、プログラム・カウンタとスタックだ けが用意されている。つまり、現在、どの部分を実行し ているかという情報と局所変数だけが各スレッドに割り 当てられている。 大域変数は、すべてのスレッドて共有される。したがっ て、或変数を使えばスレッド間での通信カそきる。 ・スレッドの生成や消去などは、プログラムで明カ勺に記 述できる。 スレッドを利用すると、ユーサー空間て稼動するプロ グラムで並列処理を簡単に記主できる。描丘の生能サー バーの世界では、スレッドの利用を目指す開発か続けられ ている。現在のスレッドの実装には、 BSD 系の pthread や Linux の linux pthread などがある。これらはそれ ぞれに実装形態か違い、長所もあれば短所もある。また、 BSD の pthread の実装のように、システムコールとの 親和がやや悪いという欠点は解消されつつあるが、完全 、解決されたわけではない。スレッドを利用するサーバー UNIX MAGAZIN E 2002 ユ はまだ少ないが、高性能サ→ヾーの世界ではスレッドを用 いた開発が一殳的になり始めている。 WWW サーバーの Apache 2.0 系列がその一例である。今後、高生能サー ーを構築する際には、使用されるプログラム自体がスレ ノ、 ッドを使う実装になると思われる。 ☆ 今回は、十分な処理性能が得られるサーバーの構築方 法について述べた。むろん、厳選された性能のよいパーツ を用いて高生能サーバーを構築するガ去もあるが、レイヤ 4 / 7 スイッチを使ったクラスタ化によって実現すること も増えてきている。とくに、商用 WWW サービスを提供 するサイトなどでは、常識といってもよいはどクラスタ化 サーバーの利用が進んでいる。 高生能サーバーを導入する場合は、レイヤ 4 / 7 スイツ チを用いたサーバー構成のノウハウを応用するとよい。最 近は、サーバー・プログラムそのものの高性能化もスレッ ドを用いた並列処理によって見されようとしている。ス レッドを用いた処理は SMP 型システムとの整合生もよ く、今後、高性能サーバー・プログラムの実装手法とし て普及する可能性がある。サーバーの実装を目指すソフト ウェア技術者は、スレッドを用いたプログラム開発の腕を 磨くべきであろう。 ( やまぐち・すぐる奈良先立斗 ! 物支彳大芋ギ完大学 ) [ 文献 ] [ 1 ] Lawrence S. Brakmo and Larry L. Peterson, "TCP Vegas: End t0 End Congestion Avoidance on a Global lnternet ” IEEE ノ 0 社ロ田 1 0 れ Selected Areas をれ Comm 社れを ca 0 れ , V01.13 , NO. 8 , Oct0ber 1995 (ftp: //ftp.cs.arizona.edu/xkernel/Papers/jsac.ps. Z) 65

9. UNIX MAGAZINE 2002年1月号

表 3 さまざまな構成の PC -733 でデータを週言したときの合計アイドル時間俿畴合 nettest 実行中のアイドル時間 PC ー 733 ・商化 VM/PC-733 IRQ 通知をおこなわない最適化 VM/PC-733 送信の結合と IRQ 通知をおこなわない最商化 VM/PC-733 VMware Workstation 2.0 の VM/PC-733 ク・リンクを一杯にするかなり前に CPU によって性能が 制限される。しかし、最商化を施すと VM/PC-733 はネ イテイプに匹敵するスルーブットを達成する。 VM/PC- 350 は最適化を施しても CPU の性能による制限を受け るが、 2 倍のスルーブットを維持し、最適化されていな い V / PC ー 733 と同等の性能を達成している。 VM/PC -350 の 2 つの曲線はそれぞれ PC ー 733 の曲線の形状と一 致する。 図 6 は、 VM/PC-733 が CPU の性能による制限を受 けすに 100Mbit のリンクを一杯にできることを示してい る。しかし、 VM/PC-350 は最直化を施しても CPU に よって性能を左右される。ネイテイプの PC ー 733 と pc- 350 は 100Mbit のリンクを簡単に一にする。最後の実 験では、さまざまな構成で CPU がどのように消費される かについての情報を集めた。 システムを詩ヾ、アイドル日判の正確な長さを得た。通 常は、ゲストカ止命令 (HLT) を発行すると、 VMware Workstation は領域を VMApp に切り替える。そして、 VMApp はすべてのテンヾイスで select() をプロックする。 こでは、ホスト OS に制御を戻す代わりに、ゲストの HLT 命令がスピンして VMM の CPU を停止させるオ プションを有効にした。 TSC レジスタを使用して、ゲス トが HLT 命令を発行してから次のハードウェア割込み が発生するまでのアイドル時間を測定した。このアイドル 時間は、ゲスト OS がほかの計算をするのに利用できる CPU サイクルである。ホスト OS のはうは、このアイド ル時間をすべてほかの引算に利用できるわけではない。ゲ ストの HLT 命令によって VMApp への切替えが発生 した場合、いくっかの領域切替えやシステムコールによる オーバーヘッド (select() システムコールなど ) が発生す るからである。 ネイテイブマシンのアイドル時間については、 Linux 3.5 CPU 利用率 170 86 % 21.7 % 17.9 % 2.0 % 0 % カーネルに組み込まれている標準のプロファイラを拡張し て、ユーサーコードの実行やカーネルのアイドルルーフに 使われる時間を測定できるようにした。これによって、ア イドルループに費やされた合言判間の割合カ昇られる。 表 3 に示すように、送信サイズが 4KB の VM/PC- 733 では、 64Mbps で CPU に性能を制限されていたゲ ストが、 I/O を一杯にして 21.7 % のアイドル時間がある 状態にまて改善された。これに対し、 PC -733 のアイドル 時は 86 % である。 こまでくると、残りのほはすべての オーバーヘッドは、 CPU のイ反想化か、ホスト協調型アー キテクチャの生質によるものである。次節では、ホスト協 調型アーキテクチャの枠の内側と外側で、さらなる描商化 の可能性を検証する。 前節て解説した最適化では、イ反想化による CPU のオー バーヘッドを、一ト分な I/O 性能か得られるところまて減 らすことができた。本節では、 I/O 性能をさらに改善し、 CPU の利用率を減らすための手法について説明する。お もな最適化の対象としては、 1. CPU と割込みコントローラの仮想化のオーバーヘッド の轤成 2. ゲスト OS やそのドライバの修正 3. ホスト OS の修正 4. VMM からネイテイプ・ハードウェアへの直接アクセス 力げられる。後半の 2 つの手法は、純粋なホスト協調 型イ廨課マシン・アーキテクチャからは逸脱している。ホス ト協調型アーキテクチャの言 t では、既存のホスト os が 通常どおりに実行さイ廨課ソフトウェアがホスト OS の API を使ってハードウェア・テンヾイスにアクセスすると いうことを思い出してほしい。 UNIX MAGAZINE 2002.1

10. UNIX MAGAZINE 2002年1月号

連載 /UNIX Communication Notes—O との受信侍ち状態に戻り、もう 1 つのプロセスは処理をお こなうかたちになっていた。これを擬似コードて表現する と、次のようになる。 main() 初期処理 ; socket() ; S bind(s, listen(s) ; while ( 1 ) { / * socket の生成 * / / * s 。 cket に対する設定 * / accept(s, C S exit(O) ; close(cs) ; 実際の処理 ; close(s) ; / * 子プロセス * / if ((pid = fork() / * 受信待ちでプロック このコードからも分かるように、子プロセスがクライア / * 親プロセスは実際に接続されたソケットをクローズ * / / * 受信待ちに戻る * / close(cs) ; システムコールを用いて起動するかたちにした。 inetd に 理するプログラムを inetd が fork() し、さらに exec( 特定のポートへのアクセスがあった場合、そのポートを処 をしなければならない複数のポートを監視する。そして、 inetd は、 select() システムコールを使って、受信待ち UNIX で標準的に使われるようになった。 inetd (lnternet super daemon) が開発され、 4.3BSD せておかなけれはならないという間題を解決するために 上記の間題のうち、あまり使われないデーモンも不力さ inetd の登場 かった。 ・プロセスを複製する fork() システムコールの実行が遅 った。 モリしかなかった衫琪月の UNIX システムには重荷とな として、プロセステープルか増えてしまい、わずかなメ め、デーモンとして稼動させるプロセスか増える。結果 ・ふだん利用しないサーバーもデーモンとして起動けるた この方式には、以 - ドのような間題があった。 のソケット接続要求だけを処理する構造になっている。 ントからの要求を処理し、親プロセスはクライアントから 64 よって起動される処理プログラムは、標準入力からデータ を読み込むと socket に届いたデータを読むことができ、 標準出力に書き出すと相手にデータか送られるような形式 UNIX MAGAZINE 2002.1 グラムのなかでうまく処理できるようにすれはよい。この きなオーバーヘッドをともなう実装ではなく、 1 つのプロ だけに fork() でプロセスを複製している。このように大 さきはど窈疑似コードでは、処理をおこなう部分のため ラムが考案された。 fork() によるプロセスの複製はしないサーバー・プログ 間で通常のデーモン・プログラムとして実行されるが、 その後カーネルに組み込むのではなく、ユーサー空 ユーサー空間で作る ネルに組み込まれることはあまりなかった。 くい。そのため、 NFS 以外のサーバー・プログラムがカー への糾込みは、作業か難しいだけでなく、デバッグもしに しかし、一殳的な処理をおこなうプログラムのカーネル ていた。 ファイルシステムを扱う NFS はカーネルへの実装に適し ロセスの複製というオーバーヘッドを軽減した。しかも、 常駐スレッド (thread) をカーネルに組み込むことで、プ しようと考えたわけである。 NFS では、処理をおこなう るプロセスの複製はしないように実装し、処理性能を改善 変わりはなかった。そこで、常駐はしても、 fork() によ って複製される状況は、 inetd かイ吏われるようになっても サーバーへのアクセスがあるとプロセスが fork() によ これは、 NFS サーバーの実装の際に採られた方法である。 次に考えられたのが kernel resident thread である。 kernel resident thread えなかった。 にデーモンとして起動さ常駐するという形態をとらざるを などの常駐すべきサーバーは、依然としてシステムのプート時 けではない。たとえば、 UDP を用いたサービスや、 sendmail しかし、すべてのデーモンが inetd の管理下に置かれたわ ノート 5 わすかながら改善された。 状態にしておくという間題カ黝夬さシステムの性能が きる。これにより、起動された大量のデーモンを受信待ち peername() などのライプラリ関数を用いて知ることがで の API が定められた。相手のアドレスなどの情報は、 get-