next up previous contents
Next: ネットワークプログラムを書く Up: プロセス間通信をする Previous: ソケット

並列入出力

ソケットを複数使うようになると困った問題が起きてきます。UNIXは実時間 OSではありませんから入出力を行なう場合も一つのプロセスでは同時には一つ しか処理できません。サーバプログラムなどでソケットを次々とacceptしても 同時に一つのファイル記述子にしかrecvを発行できない。その入出力が完了し ないかぎり次のソケットを見にいけないということになってしまいます。これ はオンラインソフトウエアを書く場合に非常に都合が悪いわけです。そこで、 recvを発行する前にどのソケットにデータが到着しているかを調べる手段が提 供されています。データを持っているソケットだけを見に行けばただちにデー タを返してくれるのでなんとか目的を達することが出来ます。

このために使われるシステムコールはselectです。selectではソケットに限 らずシステムコールでファイル記述子を用いる色々な入出力にも適用できます。 selectシステムコールではファイル記述子セットという構造体を用いてどの入 出力を調べるかを指定します。関数定義は次のようになります。

        int select( width, readfds, writefds, exceptfds, timeout )

        int width;

        fd_set * readfds, * writefds, * exceptfds;

        struct timeval * timeout;

widthはファイル記述子セットの大きさを与えるものでFD_SETSIZEとします。 readfdsは入力動作を調べる記述子セット、writefdsは出力動作を調べる記述 子セット、exceptfdsは例外発生を調べる記述子セットです。必要のない記述 子セットは0で置き換えて構いません。記述子セットは、selectを呼び出す前 に調べてもらいたい記述子のビットを立てておきます。selectから返ってくる と要件を満たした(例えば入力データを持っている)記述子のビットだけが立っ ています。timeoutは時間切れを設定するタイマーで0を指定すると入出力が発 生するまで待ちます。0に設定されたtimeval構造体へのポインターを与えると ただちに制御が戻ってきます。

ファイル記述子セットを操作するためのマクロがいくつか用意されています。

        FD_ZERO( & fdset )      記述子セットをゼロで初期化します。

        FD_SET( fd, & fdset )   fdの記述子に対応するビットをセットします。

        FD_CLR( fd, & fdset )   fdの記述子に対応するビットをクリアします。

        FD_ISSET( fd, & fdset ) fdに対応するビットが立っているか調べます。

ソケットが二つ開いているとしてselectを使って受信を行なう例を見てみましょ う。

        #include        <sys/types.h>

        #include        <sys/time.h>



        struct sockaddr sa1, sa2;

        int             sal1, sal2, fd1, fd2;

        fd_set          readfds;

        struct  timeval timeout;



        sal1 = sizeof( sa1 );

        fd1 = accept( fd, & sa1, & sal1 );      /* ソケット1 */

        sal2 = sizeof( sa2 );

        fd2 = accept( fd, & sa2, & sal2 );      /* ソケット2 */



        FD_ZERO( & readfds );   /* 記述子セットを初期化 */



        while( 1 )      /* 読み出し用無限ループ */

        {

            FD_SET( fd1, & readfds );           /* fd1をセット */

            FD_SET( fd2, & readfds );           /* fd2をセット */

            timeout.tv_sec = 0;         /* タイムアウトを */

            timeout.tv_usec = 1000000;  /* 0.1秒に設定 */

            i = select( FD_SETSIZE, & readfds, 0, 0, & timeout );

            if( i )             /* 該当するものがあった */

            {

                if( FD_ISSET( fd1, & readfds ) )

                /* fd1がデータを持っていれば */

                {

                    len = recv( fd1, buf, 256, 0 );

                  /* それを読みだす。 */

                }

                if( FD_ISSET( fd2, & readfds ) )

                /* fd2がデータを持っていれば */

                {

                    len = recv( fd2, buf, 256, 0 );

                    /* それを読みだす。 */

                } 

            }

        }

VMSのように割り込みのユーザ完了ルーチンを宣言して非同期に実行する機能 がUNIXにはありませんので、このようにデータがあるかどうかぐるぐる見て回 るポーリング手法を使うことになるわけです。



next up previous contents
Next: ネットワークプログラムを書く Up: プロセス間通信をする Previous: ソケット



Kinya Hibino
Sun Jan 14 21:36:40 JST 1996