본문 바로가기
코스웨어/11년 내장형하드웨어

[내장형]하드웨어 최성태

by 알 수 없는 사용자 2011. 7. 25.
728x90
반응형

<멀티플렉싱>

<select함수>











- 멀티플렉싱기반으로 통신할 때 프로그램은 select()함수에게 입/출력이 예상되는 소켓의 식별자 리스트를 명시한다. select()는 리스트의 식별자 중의 하나가 입/출력을 시행할 준비가 될 때까지 프로그램을 중단시키고 어떤 식별자가 준비가 되었는지(식별자의 개수)를 반환하여 알린다. 그 후에 프로그램은 그 식별자에 대한 입/출력작업이 블로킹되지 않을 것이라는 확신을 가지고 진행할 수 있다.

int select(int maxDescPlus1,fd_set *readDescs,fd_set *writeDescs,fd_set*exceptionDescs,struct timeval *timeout)

select()는 세 종류의 식별자 리스트를 검사하는데,
1. readDescs : 이 리스트에 있는 식별자들은 시스템에 의해 즉시 입력이 가능한지 확인된다. 즉, 입력가능으로 인해 반환된 식별자에 대해 recv()(또는 recvfrom())는 블로킹되지 않는다.
2. writeDescs : 이 리스트에 있는 식별자들은 시스템에 의해 즉시 출력이 가능한지 확인된다. 즉, 출력 가능으로 인해 반환된 식별자에 대해 send()(또는 sendto())는 블로킹되지 않는다.
3. exceptionDescs : 이 리스트에 있는 식별자들은 시스템에 의해 예상되는 예외 사항이나 에러가 발생했는지 확인된다.

세가지 식별자 리스트 위치 어느 곳에서라도 NULL을 전달하면 select()는 대상 리스트에 대한 입/출력 감시를 하지 않는다. 예를 들어, exceptionDescs위치에 NULL값을 전달하면 select()는 어떤 소켓에 대해서도 오류나 예외 사항을 검사하지 않는다.

void FD_ZERO(fd_set *descriptorVector)
void FD_CLR(int descriptor,fd_set *descriptorVector)

void FD_SET(int descriptor,fd_set *descriptorVector)
void FD_ISSET(int descriptor,fd_set *descriptorVector)


FD_ZERO()는 식별자 리스트를 비운다. FD_SET()와 FD_CLR()는 리스트에 특정 식별자를 추가하거나 제거한다. FD_ISSET()는 리스트에 특정 식별자의 값이 설정되어 있는지 확인하고 주어진 식별자가 리스트에 있으면 0이 아닌 값을 반환하며, 그렇지 않으면 0을 반환한다.

리스트에 담을 수 있는 식별자의 최대 개수는 시스템 정의 상수인 FD_SETSIZE에 정의되어 있다. 상수에 정의된 숫자는 상당히 크지만 대부분의 응용프로그램에서 실제로 검사를 원하는 식별자의 개수는 상당히 적다. 매번 FD_SETSIZE만큼 검사하면 비효율적이므로 시스템은 이를 효율적으로 처리하기 위해서 힌트가 되는 정보를 select()함수가 첫번째 파라미터로 받아들이도록 했다. 이 힌트의 값은 입/출력 검사를 위해 세가지 리스트에 추가된 식별자 중에서 가장 값이 큰 식별자 번호를 나타내어 시스템이 검사하는 식별자의 범위를 제한한다. 다른 말로 maxDescPlus1의 값을 최대 식별자값인 5에 1을 더한 값으로 설정하면 된다.
maxDescPlus1값은 세 가지 리스트에 모두 적용된다는 것에 주목하자. 만약 예외를 검사하는 리스트에 추가된 가장 큰 식별자의 값이 7이고 읽기와 쓰기를 검사하려는 리스트에 추가된 최대 식별자의 값이 각각 5와 2라면, 이때 maxDescPlus1은 8로 설정해야 한다.

최대 세 종류의 입/출력 검사를 위해 동시에 아주 많은 식별자들을 확인하기 위한 기능이 필요하면 어떻게 할까? 걱정할 필요가 없다. select()는 이 문제도 잘 처리해 낸다. 마지막 파라미터인 timeout은 select()가 얼마나 오랫동안 입/출력 검사를 한 후에 반환되어야 하는지를 나타낸다. timeout인자는 timeval자료 구조체에 명시된다.
struct timeval
{
time_t tv_sec; //Seconds
time_t tv_usec; //Microseconds
};

만약 timeval 구조체 명시된 시간이 어떤 특정 식별자들이 입/출력에 준비가 되기 전에 지나버린다면, select()는 0을 반환한다. 만약 timeout이 NULL로 전달되면, select()는 시간제한없이 적어도 하나의 식별자가 파일 또는 소켓의 입/출력 준비가 될 때까지 대기한다. tv_sec와 tv_usec를 0으로 설정하면 select()는 리스트에 추가된 입/출력 식별자들을 검사할 수 있도록 즉시 반환된다.
에러가 발생하지 않으면 select()는 입/출력 준비가 된 식별자의 모든 개수를 반환한다.
select()는 리스트 내에서 입/출력 준비가 된 식별자의 위치에 해당하는 비트는 1로, 준비가 되지 않은 식별자의 위치에 해당하는 비트는 0으로 설정하여 그 둘을 구별한다. 예를 들어 0,3,5 식별자가 readDescs리스트에 추가되어 있고 쓰기와 에러를 위한 리스트는 NULL로 설정되어 있다고 하자. 이 때 0과 5번 식별자가 가리키는 파일 또는 소켓에 읽기 준비가 되면 select()는 2를 반환하고, readDescs리스트에서는 0과 5에 해당하는 비트를 1로 설정하며, 그 밖에 비트는 0으로 설정한다. 에러가 발생하면 select()는 -1을 반환한다.



// select.c

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

#define BUFSIZE 30

int main(int argc, char *argv[])
{
fd_set reads, temps;
int result;
char message[BUFSIZE];
int str_len;
struct timeval timeout;

FD_ZERO(&reads); //0
으로 초기화
FD_SET(0,&reads); //
파일디스크립터 0(stdin)설정

/*
timeout.tv_sec
= 5;
timeout.tv_usec
= 100000;
*/
//
잘못된 timeout 설정

while(1)
{
temps = reads; //
원본 보존위해 복사본 temp 이용

timeout.tv_sec = 5; //timeout
설정
timeout.tv_usec = 0;

result = select(1,&temps,0,0,&timeout);
if(-1 == result) //select()
오류 발생
{
puts("select() :
오류발생");
exit(1);
}
else if(0 == result) //
타임아웃에 의한 리턴
{
puts("select() :
시간이 초과되었습니다. ");
}
else //
파일디스크립터 변화에 의한 리턴
{
if(FD_ISSET(0,&temps))
{
str_len = read(0,message, BUFSIZE);
message[str_len] = 0;
fputs(message,stdout);
}
}
}
return 0;
}

<소스 결과>

728x90