selectシステムコール


多重化されたI/Oの同期をとる。すなわち、複数のファイル・ディスクリプタ(オープンされたファイルまたはデバイス)を監視し、それらの変化を待つUNIXのselectシステムコール。 機能の詳細は端末エミュレータ上でman selectを実行しman ページを参照せよ。

selectシステムコールを使って複数の入力を監視するサンプル1server_sel.cを示す。

selectシステムコールの使用例1
/******************************************************************/
/*** server_sel.c                                               ***/
/*** 入力: クライアントからの接続要求                           ***/
/***      (7個までのクライアントの接続に対応)                   ***/
/*** 出力: 約1秒毎に、接続中の全クライアントに対して、         ***/
/***       カウントダウン中のカウンタの値をメッセージとして送信 ***/
/******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/unistd.h>

#define MAX_SOCKS 9

int main()
{
  int s;
  int ss;
  
  int addrlength;
  char write_buf[1024];
  
  int k;

  /************************************************************/
  /* ソケットを格納する配列                                   */
  /* socks[0] には listening ソケットを格納                   */
  /* socks[1...(last_socks-1)] には accept したソケットを格納 */
  /************************************************************/
  int socks[MAX_SOCKS];
  int last_socks;  

  /****************************************/
  /* 入力のファイル・ディスクリプタの集合 */
  /****************************************/
  fd_set rfds;
  
  /***********************************************************/
  /* select() から返ってくるまでの時間の上限を設定する構造体 */
  /***********************************************************/
  /* struct timeval {                                        */
  /*   long     tv_sec;     /* 秒                            */
  /*   long     tv_usec;    /* マイクロ秒                    */
  /* };                                                      */
  /***********************************************************/
  struct timeval tv;

  /* カウントダウンのためのカウンタ */
  int count;

  /* 時間を計るためのタイマー */
  int timer;
  
  /***************************************************************/
  /* ソケットアドレス UNIX                                       */
  /***************************************************************/
  /* struct sockaddr_un {                                        */
  /*  sa_family_t     sun_family;                     /* AF_UNIX */
  /*  char            sun_path[UNIX_PATH_MAX];       /* pathname */
  /* };                                                          */
  /***************************************************************/
  struct sockaddr_un addr;
     
  /****************************************/
  /* ソケット、通信のための端点の作成     */
  /* PF_UNIX: UNIX ローカル通信           */
  /* SOCK_STREAM: 順序性と信頼性があり、  */
  /*              双方向の、接続された    */
  /*              バ イ ト・ ス ト リーム */
  /****************************************/
  s = socket(PF_UNIX, SOCK_STREAM, 0);
  if (s == -1) {
    printf("Can not create socket.\n");
    exit(1);
  }

  /******************************/
  /* アドレスの設定             */
  /* AF_UNIX: UNIX ローカル通信 */
  /* 名前: mysocket             */
  /******************************/
  addr.sun_family = AF_UNIX;
  strcpy(addr.sun_path, "mysocket");

  /************************************/
  /* ソケットに名前、アドレスを与える */
  /************************************/
  addrlength = sizeof(addr);
  if (bind(s, (struct sockaddr *)&addr, addrlength) == -1) {
    printf("Can not assign the address to a socket.\n");
    exit(1);
  }

  /****************************/
  /* ソケット上の接続を待つ   */
  /* 接続を待機させる最大数 2 */
  /****************************/
  if (listen(s, 2) == -1) {
    printf("Can not listen for connections.\n");
    exit(1);
  }
  printf("listen...\n");

  /****************************************/
  /* listening ソケットを socks[0] に格納 */
  /****************************************/
  last_socks = 0;
  socks[last_socks] = s;
  last_socks++;

  timer = 0;    /* タイマーの初期化 */
  count = 20;   /* カウンタの初期化 */
  while (1) {
    /******************************************************/
    /* 入力のファイル・ディスクリプタの集合 rfds を clear */
    /******************************************************/
    FD_ZERO(&rfds);

    /*********************************************/
    /* 入力のディスクリプタの集合 rfds に        */
    /* listening ソケット socks[0] と            */
    /* 接続中の socks[1...(last_socks-1)] を追加 */
    /*********************************************/
    for (k = 0; k < last_socks; k++) {
      FD_SET(socks[k], &rfds);
    }

    /******************************************************/
    /* select から 100000 マイクロ秒で返ってくるように指定 */
    /******************************************************/
    tv.tv_sec = 0;
    tv.tv_usec = 100000;

    /***********************************************************/
    /* select システムコール                                   */
    /* rfds にリストされた入力のファイル・ディスクリプタを監視 */
    /* タイムアウト時間は 100 ミリ秒                           */
    /***********************************************************/
    select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
    printf(".");

    /****************************************************************/
    /* listening ソケット socks[0] に接続があったかどうかのチェック */
    /****************************************************************/
    if (FD_ISSET(socks[0], &rfds)) {    /* 接続あり */
      /* socks[] があふれないかどうかのチェック */
      if (last_socks < MAX_SOCKS) {     /* あふれない */
	/****************************/
	/* ソケット上の接続を受ける */
	/* 待ちなし                 */
	/****************************/
	ss = accept(socks[0], (struct sockaddr *)&addr, &addrlength);
	printf("\nAccepted... last_socks=%d\n", last_socks);

	/****************************************/
	/* accept したソケットを socks[] に追加 */
	/****************************************/
	socks[last_socks] = ss;
	last_socks++;
      } else {    /* socks[] があふれる場合 */
	printf("Too many socks. last_socks=%d, MAX_SOCKS=%d\n",
	       last_socks, MAX_SOCKS);
	break;
      }
    }

    /******************************/
    /* 約1秒たったかどうかの確認 */
    /******************************/
    if (timer == 10) {    /* 約1秒たった(100ミリ秒×10)*/
      timer = 0;
      /********************************************************/
      /* 接続中の全クライアント socks[1...(last_socks-1)] に  */
      /* カウントダウン中のカウンタの値をメッセージとして送信 */
      /********************************************************/
      for (k = 1; k < last_socks; k++) {
	/****************/
	/* write で送信 */
	/****************/
	sprintf(write_buf, "%d\n", count);
	printf("write_buf=%s\n", write_buf);
	ss = socks[k];
	write(ss, write_buf, strlen(write_buf)+1);
      }

      /*********************************************/
      /**** カウンタが0になったかどうかのチェック */
      /*********************************************/
      if (count == 0) {    /* なった */
	count = 20;
	break;
      } else {
	count--;
      }
    } else {    /* まだ1秒たたない */
      timer++;
    }

  }

  /**********************/
  /* ソケットのクローズ */
  /**********************/
  shutdown(s, 2);
  close(s);
  unlink("mysocket");
}

selectシステムコールを使って複数の入力を監視するサンプル2client_sel.cを示す。

selectシステムコールの使用例2
/************************************/
/*** client_sel.c                 ***/
/*** 入力: サーバからのメッセージ ***/
/*** 出力: サーバへの接続要求     ***/
/************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

int main()
{
  int s;
  int addrlength;
  char read_buf[1024];

  /****************************************/
  /* 入力のファイル・ディスクリプタの集合 */
  /****************************************/
  fd_set rfds;

  /***********************************************************/
  /* select() から返ってくるまでの時間の上限を設定する構造体 */
  /***********************************************************/
  /* struct timeval {                                        */
  /*   long     tv_sec;     /* 秒                            */
  /*   long     tv_usec;    /* マイクロ秒                    */
  /* };                                                      */
  /***********************************************************/
  struct timeval tv;

  /***************************************************************/
  /* ソケットアドレス UNIX                                       */
  /***************************************************************/
  /* struct sockaddr_un {                                        */
  /*  sa_family_t     sun_family;                     /* AF_UNIX */
  /*  char            sun_path[UNIX_PATH_MAX];       /* pathname */
  /* };                                                          */
  /***************************************************************/
  struct sockaddr_un addr;
     
  /****************************************/
  /* ソケット、通信のための端点の作成     */
  /* PF_UNIX: UNIX ローカル通信           */
  /* SOCK_STREAM: 順序性と信頼性があり、  */
  /*              双方向の、接続された    */
  /*              バ イ ト・ ス ト リーム */
  /****************************************/
  s = socket(PF_UNIX, SOCK_STREAM, 0);
  if (s == -1) {
    printf("Can not create socket.\n");
    exit(1);
  }

  /******************************/
  /* アドレスの設定             */
  /* AF_UNIX: UNIX ローカル通信 */
  /* 名前: mysocket             */
  /******************************/
  addr.sun_family = AF_UNIX;
  strcpy(addr.sun_path, "mysocket");

  /**********************/
  /* ソケットに接続する */
  /**********************/
  addrlength = sizeof(addr);
  if (connect(s, (struct sockaddr *)&addr, addrlength) == -1) {
    printf("Can not initiate a connection.\n");
    exit(1);
  }
  printf("connected.\n");

  while (1) {
    /******************************************************/
    /* 入力のファイル・ディスクリプタの集合 rfds を clear */
    /******************************************************/
    FD_ZERO(&rfds);

    /**************************************/
    /* 入力のディスクリプタの集合 rfds に */
    /* 接続中の s を追加                  */
    /**************************************/
    FD_SET(s, &rfds);

    /******************************************************/
    /* select から 100000 マイクロ秒で返ってくるように指定 */
    /******************************************************/
    tv.tv_sec = 0;
    tv.tv_usec = 100000;

    /***********************************************************/
    /* select システムコール                                   */
    /* rfds にリストされた入力のファイル・ディスクリプタを監視 */
    /* タイムアウト時間は 100 ミリ秒                           */
    /***********************************************************/
    select(FD_SETSIZE, &rfds, NULL, NULL, &tv);

    /**********************************************/
    /* s が待ちなしで読み込めるかどうかのチェック */
    /**********************************************/
    if (FD_ISSET(s, &rfds)) {    /* 待ちなしで読める */
      /****************************/
      /* s からのデータの読み込み */
      /****************************/
      read(s, read_buf, sizeof(read_buf));
      printf("read_buf[]=%s\n", read_buf);
      if ((read_buf[0] == '0') && (read_buf[1] == '\n')
	  && (read_buf[2] == '\0')) {
	printf("finished.");
	break;
      }
    }

  }
  
  /**********************/
  /* ソケットのクローズ */
  /**********************/
  shutdown(s, 2);
  close(s);
}


中村 一博 E-mail: nakamura@is.nagoya-u.ac.jp