반응형


- 본 내용은 Linux (Ubuntu 14.04 lts)를 기반으로 제작되었습니다. -





- I/O Multiplexing


예를들어 네트워크 프로그램을 구현하다 보면, scanf중에는 printf가 되지 않는 현상들을 볼 수 있다.

즉, I/O가 병행적으로 진행되지 못하여 (I/O Blocking 발생) 그런 것인데, 이러한 방법을 해결하기 위해 다양한 방법들이 나타났다.


그중 Fork, 스레드에 대해서 이전 게시물에서 다루어 보았고, 

이번에는 하나의 프로세스, 하나의 스레드를 이용하는 I/O Multiplexing에 대해 다루어 볼 예정이다.


I/O Multiplexing을 구현하기 위해서 이용되는 함수는 select, poll, epoll, kqueue등이 있다.


I/O Multiplexing 기능은 하나의 프로세스여러 파일 디스크립터를 모니터링해서 

어떤 종류의 I/O 이벤트가 일어났는지 검사하고 각각의 파일 디스크립터가 Ready 상태가 되었는지 인지하는게 주요 목적이다.


이중 select와 poll에 대해서만 다룰 예정인데, 이번 게시물에서는 select를 다루려 한다.



- select란?


select()는 지정한 소켓의 변화를 확인하고자 사용되는 함수이다.


즉, 소켓셋에 저장된 소켓에 변화가 생길 때 까지 기다리고 있다가 


소켓이 어떤 동작을 하면 동작한 소켓을 제외한 나머지 소켓을 모두 제거하고 해당되는 소켓에 대해 진행을 한다.



- select의 특징


select 함수는 처음에는 block되어있다가 특정 이벤트가 발생하면 그때 작동을 하는 방식이다.


1. 파일 디스크립터를 하나하나 체크 해야하고, OS와 유저 사이에 여러번의 데이터 복사가 일어난다.


2. poll과 다르게 파일 디스크립터의 수가 제한적이다.


3. 사용이 쉽고 지원 OS가 많기 때문에 이식성이 좋다.




- select 함수 사용 법


select 함수를 사용하기 위해서는 


파일 디스크립터를 설정하고 -> 거기에 맞는 셋팅을 한 후 -> 함수 호출을 한다.



>> 파일 디스크립터 설정 및 셋팅


fd_set 구조체명; :: 파일 디스크립터 구조체를 생성


void FD_ZERO(fd_set* fdset); :: 모든 FD Set을 만들고 0으로 초기화 해준다.

fdset :: 소켓셋의 주소


void FD_SET(int fd, fd_set* fdset); :: 해당하는 FD Set을 1로 만들어준다. 이 의미는 FD를 사용중이라는 의미이다.

fd :: 소켓셋에 삽입 할 소켓 파일 디스크립터

fdset :: 소켓셋의 주소


void FD_CLR(int fd, fd_set* fdset); :: 해당하는 FD Set을 0으로 만들어준다. 이 의미는 FD를 사용하고 있지 않다는 의미이다.

fd :: 소켓셋에 삽입 할 소켓 파일 디스크립터

fdset :: 소켓셋의 주소


int FD_ISSET(int fd, fd_set* fdset); :: 해당하는 FD 비트가 1인지 즉, 켜졌는지 확인하는 함수이다.

fd :: 소켓셋에 삽입 할 소켓 파일 디스크립터

fdset :: 소켓셋의 주소




>> Select 함수 호출


int select(int nfds, fd_set* readSet, fd_set* writeSet, fd_set* exceptSet, const struct timeval* timeout);


성공시 디스크립터 수 반환,

타임아웃시 0 반환

실패시 -1 반환


nfds :: 디스크립터 마지막 번호 + 1(0번 디스크립터 부터시작이니 +1을 해준다.)

readSet :: '입력 스트림에 변화가 발생했는지(수신할 데이터가 있는지) 확인'할 때 이용하는 소켓들의 정보(fd)를 전달.

writeSet :: '데이터 전송 시, 블로킹 되지 않고 바로 전송이 가능한지 확인'할 때 이용하는 소켓들의 정보(fd)를 전달.

exceptSet :: '예외가 발생했는지 확인'할 때 이용하는 소켓들의 정보(fd)를 전달.

timeout :: 함수 호출 후, select함수가 무한 대기 상태에 빠지지 않게 seconds 혹은 microseconds를 통한 시간 제한 설정



기본 예제 코드


select함수를 통해 5초동안 아무 입력이 없으면 시간 초과를 출력,

그사이 입력이 이루어지면 에코식으로 다시 출력해주는 방식.


while(1)

{

temps = reads;

...


의 의미는 소켓 셋의 값이 변한걸 다시 초기화 시켜주기 위함이다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
 
#define BUFFSIZE 30
 
int main(int argc, char *argv[])
{
    // reads와 temps 소켓 셋을 선언
    // 여기서 temps는 단지 reads의 복제용으로 이용될 뿐이다.
    fd_set reads, temps; 
    int result;
 
    char message[BUFFSIZE];
    int str_len;
    
    struct timeval timeout;
 
    FD_ZERO(&reads); // FD 생성과 동시에 0으로 초기화
    FD_SET(0,&reads); // reads 파일 디스크립터 0(stdin)으로 설정
 
    while(1)
    {
        temps = reads; // reads의 내용을 temps에 넣는다.
 
        timeout.tv_sec = 5// 5초
        timeout.tv_usec = 0;
 
        result = select(1&temps, 00&timeout); // select함수 실행
 
        if(result == -1// 실패시
        {
            puts("select() :: 오류 발생");
            exit(1);
        }
 
        else if(result == 0// 타임 아웃시
        {
            puts("select() :: 시간이 초과 되었습니다. ");
        }
        
        else // 성공시
        {
            if(FD_ISSET(0,&temps))
            {
                str_len = read(0, message, BUFFSIZE);
                message[str_len] = 0;
                fputs(message, stdout);
            }
        }
    }
 
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus






반응형