Select ()

Mokwon Univ 2008. 8. 16. 15:37

서버: select() 함수


   select() 콜은 네트워크에서 사용되는 전송 방식 중 I/O multiplexing 이란
   기능을 사용할 수 있게 해주는 함수 입니다. I/O Multiplexing 기술을 사용
   하면 여러개의 소켓을 좀더 간편하게 관리할 수 있습니다.

   실제로 사용되는 부분을 직설적으로 표현하자면 사용자 여려멍 접속을
   받아서 그 사용자로 부터의 입력, 서버에서 사용자에게로의 출력 그리고
   예외 처리를 해줄 수 있다고 할 수 있습니다.

   select() 는 Unix Network Programming 책의, 6.13 입출력 다중화 (309 페이지)
   에 보시면 자세히 서술되어 있습니다.

   그리고 저는 리눅스 서버 중심으로 설명 드리겠습니다.

   다음은 select 함수의 man page 입니다.

NAME
    select, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing

SYNOPSIS
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>

    int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
               struct timeval *timeout);

    FD_CLR(int fd, fd_set *set);
    FD_ISSET(int fd, fd_set *set);
    FD_SET(int fd, fd_set *set);
    FD_ZERO(fd_set *set);

   다음은 여기서 사용된 timeval 구조체의 멤버들 입니다.

   struct timeval
   {
        time_t          tv_sec;         /* seconds */
        suseconds_t     tv_usec;        /* microseconds */
   };

   time_t 와 suseconds_t 는 long 형의 typedef 입니다.

   그리고 tv_usec 는 "밀리세컨드" 단위가 아닌 "마이크로세컨드" 단위
   입니다. (1000000 마이크로 세컨드 == 1초, 1000 밀리 세컨드 == 1초,
   1000 마이크로세컨드 == 1 밀리세컨드)

   다음은 select 의 함수의 인자 설명입니다.

   파일 디스크립터는 fd 로 줄여서 쓰겠습니다. -_-; 그리고 소켓은 fd 에
   포함 됩니다, 고로 소켓은 fd 라고 할 수 있습니다.
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   select(int 최대 fd 값, fd_set 읽기신호, fd_set 쓰기신호, fd_set 예외신호,
          struct timeval 쉴 시간)

   최대 fd 값은 (윈도우에선 이방법 안통함 -_-) 걍 소켓은 integer 형이죠?
   그리고 그 소켓중에서 가장 큰 값이 최대 fd 값입니다.

   책에는 이렇게 설명되어 있습니다.

   "select에 대한 요청은 집합 {1,4,5} 에 있는 화일 지정번호가 읽을 준비가
    되어 있는지 또는 집합 {2,7}에 있는 화일 지정번호가 출력할 준비가 되어
    있는지, 또는 집합 {1,4}에 있는 화일 지정 번호가 미결 예외조건 (exeptional
    condition pending) 을 갖고 있는지를 우리에게 말해줄 수 있다. 추가적으로
    우리는 알맹이에게 아래와 같은 사항들을 요청할 수 있다."

    해석: (이책은 원서는 그냥 그런데 해석본이 완전 엉망임 푸하하)
          우선 알맹이는 커널(Kernel) 을 뜻합니다. 한마디로 select 함수를
          호출해 해당 fd (소켓 포함) 에서 읽어올 수 있나 출력 할 수있나
          예외가 있는가를 알아올 수 있다는 거죠. -.-;
        

    -------------
    첫번째 사용법
    -------------
    o 지정번호들을 조사한 후에 즉시 돌려주시오. 이것은 응답조사이다. 이것을
      위해 timeout 독립변수는 timeval 구조를 가리키고 있어야 한다. 시간
      측정의 값은 0 이어야 한다. (시간 측정기의 값은 구조에서 규정한 seconds
      또는 microseconds)

    - 해석:

      여기서 '지정번호들'은 최대 fd 값의 아래번호를 가진 fd 가 되겠습니다.
      그리고 시간 측정의 값은 0 이어야 한다 이말은 select() 함수의 맨
      마지막 인자 struct timeval 을 0 으로 모두 만들어서 넣어야 원하는
      방향으로 만들어 진다는 겁니다.

    - 이렇게 하려면?

      fd_set input, output, exc;
      struct timeval nulltime;

      nulltime.tv_sec  = 0;
      nulltime.tv_usec = 0;

      FD_ZERO(&input);
      FD_ZERO(&output);
      FD_ZERO(&exc);

      FD_SET(접속을 받고 있는 fd, &input);
      // 접속을 받고 있는 fd 란 서버가 띄워진 포트에 물린 소켓의 번호를
      // 뜻합니다.

      // 모든 디스크립터에 input, output, exc 를 셋팅한다.
      루프 {
         FD_SET(fd, &input);
         FD_SET(fd, &output);
         FD_SET(fd, &exc);
      }

      select(최대 fd 값 + 1, &input, &output, &exc, &nulltime);

      // 호출 후에는 모든 디스크립터가 읽기/쓰기/예외인지 검사

      // 예외를 검사
      루프 {
         if (FD_ISSET(이번fd, &exc))
             예외 발생 소켓을 끊어준다.
      }

      // 읽기 가능 검사
      루프 {
         if (FD_ISSET(이번fd, &input))
             읽기 가능 소켓에서 읽어준다.
      }

      // 쓰기 가능 검사
      루프 {
         if (FD_ISSET(이번fd, &output))
             쓰기 가능 소켓에 쓸 내용이 있다면 써준다.
      }

      위와 같은 방식으로 사용하면 되겠습니다. 윈도우에선 메세지를 받아
      처리한다는 분들도 계시는데 써보지 않아 둘중 어느 것이 속도면에서
      나을지는 잘 모르겠습니다. (처리는 훨씬 편하겠군. -_-)

    - 필요한 곳은?

    제일 중요 부분이라 할 수 있는 여러 소캣에서의 읽기가능/쓰기가능/예외를
    얻어올 때. 한마디로 서버의 중추적인 역할을 할 수 있게 할 때.

    -------------
    두번째 사용법
    -------------
    o 지정된 지정번호들중의 하나가 I/O 준비가 될 때 돌려 주되 고정된 시간
      이상을 기다리지 마시오.  이것을 위해 timeout 독립변수는 timeval 구조를
      가리키고 있어야 하며 이 값은 기다릴 시간이며 0 이 아니어야 한다.
      (시간 측정기의 값은 구조에서 규정된 seconds 또는 microseconds)
     
    - 해석:
      (이미 설명한 부분을 건너 뛰도록 하겠습니다.)    

      별거 없습니다. 그냥 timeval 구조내에 있는 시간만큼 프로세스를
      잠재운다. 입니다. -_-;

    - 이렇게 하려면?

      struct timeval timeout;

      timeout.tv_sec  = 0;
      timeout.tv_usec = 1000;

      select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout);

      결과: 1 밀리 세컨드 (1000 마이크로 세컨드) 프로세스를 잠재웁니다.

    - 필요한 곳은?
      프레임 동기화에 쓸 수 있겠습니다. sleep 같은 무식하고 조금밖에 못쉬는
      함수보단 nanosleep() 이나, usleep() 혹은 이 select() 함수를 이용해
      쉬게 하는 방법도 괜찮은 방법. 윈도우에선 Sleep() 함수로 다음과 같은
      방법으로 잠재울 수 있겠습니다.

      Sleep(timeout.tv_sec * 1000 + timeout.tv_usec / 1000);


    -------------
    세번째 사용법
    -------------
    o 지정된 지정번호들중의 하나가 I/O 준비가 될때까지 무조건 기다리다
      준비가 되었을때 돌려 주시오. 이것을 위해 timeout 독립변수는 NULL 이어야
      한다. 이러한 기다림은 신호에 의해 일시 중단될 수 있다.

    - 해석:
      최대 fd 값보다 적은 값을 가진 fd 들의 입력/출력/예외가 생길 때 까지
      프로세스를 잠재우다 생기면 함수를 리턴 시킵니다. 이렇게 하려면 select()
      함수의 마지막 인자는 NULL 로 주어야 합니다. 그리고 시그널에 의해
      리턴될 수도 있습니다.

    - 이렇게 하려면?

      fd_set input;

      select(최대 fd 값 + 1, &input, (fd_set *) 0, (fd_set *) 0, NULL);

      하면 해당 fd들에서 뭔가 발생해서 또는 시그널이 프로세스를 깨울 때
      까지 잠을 잡니다. -_-;

    - 필요한 곳은?
      접속된 사람들이 없을 때 접속된 사람들이 없을 경우 서버가 일을 할
      필요가 없을 때, 잠시 잠재우기 위해 씁니다. (sleep 모드라고도 합니다.)

    그럼 도움이 되셨길 빌며. 이만
 
출처 : http://blog.empas.com/hoperok/  시월의 주문님
Posted by 용학도리
,