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() 함수의 다섯번째 인자 타이머에 대한 처리.
'삽질미학 > C,C++' 카테고리의 다른 글
sleep 을 이용한 타이머 모듈 - RoughTimer (0) | 2017.06.22 |
---|---|
[Linux C++] 메시지큐 기반 task 처리하기 (0) | 2017.06.16 |
[Linux C] pthread_join 호출 시 메모리 충돌 (0) | 2017.06.15 |
[C++]초딩 1학년용 덧셈뺄셈 예제 만들기 (0) | 2017.05.31 |
[Linux/C++] TCP Server-Client 연결 예제 (0) | 2017.03.30 |