select() 함수를 예제보고 가져다 쓰기만 하다가 뭔가 수정만 하면 잘 안되어

이대로는 안되겠다 싶어 개념을 좀 더 파악하기로 하였다.

 

♣ fd_set 구조체

fd_set 구조체는 File Descriptor (이하 FD) 를 저장하는 구조체이다.

안에 내용을 보면 그냥 배열로 여기면 편하다.

 

구조체 내용은 OS마다 조금씩 다른 듯 하다.

내가 쓰는 Ubuntu 14 에선 /usr/include/sys/select.h 에서 위 구조체를 확인할 수 있었다.

 

♣ FD_SET

fd_set 구조체에 2와 5의 FD 를 저장한다고 하면,

두번째 비트와 다섯번째 비트가 1로 변경된다.

값 저장은 FD_SET 매크로를 쓴다.

 

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

위 testFds 구조체의 배열값을 출력해 보면,

0 1 0 0 1 0 0 0 0 ~~~

위와 같이 2번째와 5번째 비트가 1 로 세팅된다.

 

♣ FD_ZERO

언급했듯이 fd_set 구조체의 주요 변수는 배열이다.

fd_set testFds ; 와 같이 선언만 하고 안에 배열값을 찍어보면 쓰레기값이 있을 수 있다.

 

이에 구조체를 초기화 해야 하며 아래와 같이 한다.

FD_ZERO(&testFds) ;

 

위에 FD_SET 예제에서도 변수 선언 후, 초기화를 해 주어야 한다.

 

fd_set testFds ;

FD_ZERO(&testFds)

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

♣ FD_CLR

FD_SET(2, &testFds) ;

FD_SET(3, &testFds) ;

FD_SET(5, &testFds) ;

 

위와 같이 입력 후, testFds 는 아래와 같이 세팅된다.

0 1 1 0 1 0 0 0 0 ~~~

 

이 때 만약 3번 FD 를 지우고 싶으면 FD_CLR 매크로를 쓴다.

FD_CLR(3, &testFds) ;

 

결과는 아래와 같다. 세번째 값이 0으로 초기화 되었다.

0 1 0 0 1 0 0 0 0 ~~~~

 

♣ FD_ISSET

이 매크로는 아래 select 함수와 함께 설명한다.

 

 

♣ select()

select 함수는 fd_set 구조체에 할당된 FD 의 이벤트가 발생하면

이를 감지하고 어떤 FD 이벤트가 발생했는지 알려준다.

 

ex)

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

select(6, &testFds, NULL, NULL, NULL) ;

 

이는 FD 이벤트는 6 미만값만 체크하며,

6 미만 중 2와 5 FD 의 이벤트가 발생했을 때만 깨어난다.

 

만약 2번 FD 의 이벤트가 발생하여 select 함수가 깨어나고,

select 함수 이후에 testFds 를 찍어보면,

 

0 1 0 0 0 0 0 0 0 ~~~~~~~

식으로 값이 변경된다. -> 이걸 몰라 한참 헤맸다.

 

즉 testFds 에 2, 5 FD 를 저장한 후,

저장된 FD (2,5) 중 이벤트가 발생하면,

해당 이벤트만 1 로 남기고 모두 0 으로 세팅한다.

 

이제 어떤 FD의 이벤트가 발생했는지 체크하여 FD_ISSET 매크로를 사용한다.

 

if(FD_ISSET(2, &testFds))

{

  // 2번 FD 가 발생했다.

}

 

if(FD_ISSET(5, &testFds))

{

  // 5번 FD 가 발생했다.

}

위와 같이 해당 FD에 대한 후처리를 진행하면 된다.

 

 

♣ 틀 정립

select 함수는 위 예제처럼 testFds 를 변경시킨다.

그럼 select 함수 전이나 후로 매번 testFds 를 다시 세팅해 주어야 한다.

 

이에 아래와 같은 틀이 될 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fd_set readFds ;
FD_ZERO(&readFds) ;
FD_SET(2, &readFds) ;
FD_SET(5, &readFds) ;
 
while(1)
{
      // select 함수에서 fd_set 이 변경되니 원본 대신 복사본을 select 함수에 전달한다.
      checkFds = readFds ;
 
      printf("sleep - select()\n") ;
      select(6, &checkFds, NULL, NULL, NULL) ;
      printf("wake up - select()\n") ;
 
      if(FD_ISSET(2, &checkFds))
      {
          // 2번 FD 이벤트에 대한 사후 처리.
      }
 
      if(FD_ISSET(5, &checkFds))
      {
          // 5번 FD 이벤트에 대한 사후 처리.
      }
}

 

♣ 정리

이런 저런 코드를 보면서 원본 (readFds) 를 checkFds 로 복사하는 이유를 몰랐는데

select() 함수 내에서 fd_set 구조체 값을 변경하기 때문이다.

 

이를 위와 같이 정리하며 아래 내용이 빠져있다.

  • select() 함수의 리턴값 처리
  • select() 함수의 첫번째 인자 max FD 값의 처리
  • select() 함수의 세번째, 네번째 인자 writeFds, exceptFds 에 대한 처리.
  • select() 함수의 다섯번째 인자 타이머에 대한 처리.

 

+ Recent posts