2013. 10. 28. 17:36 C언어
다중 입출력 함수 select
// 다중 입출력 함수(select(), pselect(), poll(), ppoll())
//
// 다중 입출력은 여러 fd 를 동시에 차단하면서 fd 중 하나가 차단 없이 읽고 쓸 준비가 될 때 알려주는 기능
// 다중 입출력을 위한 기본 방식
// 1. fd 중 하나가 입출력이 준비될 때를 알려준다.
// 2. 하나 이상의 fd 가 준비될 때까지 잠든다.
// 3. 어떤 fd 가 준비되었는지 확인 후 준비가 되면 깨어난다.
// 4. 차단 없이 모든 fd 가 입출력을 준비하도록 관리한다.
// 5. 1단계로 돌아가서 다시 시작한다.
//
// select()
// 한 곳에 모아 놓은 여러 file descriptor를 동시에 관찰할 수 있음
// 수신한 데이터를 지니고 있는 file descriptor가 어떤 것들인지,
// 데이터를 전송할 경우 blocking 되지 않고 바로 전달 가능한 file descriptor는 어떤 것들인지,
// 또한 예외가 발생한 file descriptor는 어떤 것들인지 정도가 관찰 내용이 됨
//
// 함수 호출 과정
// descriptor 설정, 검사 범위 설정, time out 설정
// => select 함수 호출
// => 결과 확인
//
// select 함수는 지정된 file descriptor의 변화를 확인함,
// 즉, file descriptor를 통하여 테이터 송,수신 가능 여부를 체크하는것
// 또한, I/O 버퍼를 생성했다는 것과 같은 말이자,
// file descriptor의 변화는 I/O 버퍼에 변화가 생겼다는말
// 만약 file descriptor에 변화가 생기지 않았다면 변화가 발생할 때까지 select 함수가 돌아가니
// 그 동안에는 blocking에 걸리게 됨, 이점을 해결하기 위해서 time out을 걸어줌
// time out을 설정해 놓으면 그 시간만큼 시간이 지나 return 되기 때문에 bloking 상태를 피할 수 있음
//
// 관찰 항목이 세가지 이므로 file descriptor의 묶음도 세가지로 준비해야함
// file descriptor를 세묶음으로 모아 놓기 위해서 사용되는 것이 fd_set 데이터 타입의 자료형
// fd_set은 0, 1 을 나타내는 배열이라 생각하면 됨
typedef struct fd_set{
u_int fd_count;
SOCKET fd_array[FD_SETSIZE]
} fd_set;
// fd_count : 설정하는 socket 번호.
// fd_array : 설정된 socket 배열.
//
// fd 가 입출력을 수행할 준비가 되거나 정해진 시간이 결과할 때까지 차단
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int mafdl, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
// 성공 시 반환 값 : 준비된 fd 개수
// 준비된 fd가 있을 때 준비된 fd 갯수를 반환
// fd 집합은 준비된 fd에 해당하는 비트들만 켜진 상태가 됨
// 시간 만료시 반환값 : 0
// 지정된 fd 중 하나가 준비되기 전에 시간이 만료되면 0을 반환
// 이 경우 fd 세 집합 비트들은 모두 0이 됨
// 오류 시 반환 값 : -1
// 지정된 fd 중 하나가 준비되기 전에 신호가 잡혀서 select 호출이 가로채여 -1이 반환
// 이 경우 fd 세 집합의 비트들은 모두 수정되지 않음
// ===========================================================================================================
// timeout : 만료 시간을 담은 구조체
// NULL 로 설정하면 무한정 기다림, fd 중 하나가 준비되거나 신호가 잡힐 때까지 차단
// 0 으로 설정하면 전혀 기다리지 않음, 차단없이 fd 상태만 확인하는 경우에 쓰임
// 0 이 아닌 경우 지정된 sec나 usec 만큼 기다림, fd 중 하나가 준비되거나 시간이 만료되면 반환
// 만약 time out을 설정하지 않을 경우 NULL값을 포인터 인자로 넘겨 주면 됨
#include <sys/time.h>
struct timeval {
long tv_sec; // 초
long tv_usec; // 마이크로 초
};
// ===========================================================================================================
// readfds : 자료 읽기가 준비되었는지 확인할 fd
// 즉, 입력 스트림에 변화가 발생했는지 확인(수신할 데이터가 있는가)
//
// writefds : 자료 쓰기 연산을 끝낼 수 있는지 확인할 fd
// 즉, 데이터 전송시 blocking 되지 않고 바로 전송이 가능한가
//
// exceptfds : 예외가 발생했는지 확인할 fd
// ===========================================================================================================
// fd 집합의 자료 형식은 fd_set으로, fd_set에 fd들을 할당하고 확인하는 방법은 다음의 매크로를 사용해야함
// 즉, fd_set 변수를 조작하는데 사용되는 함수
#include <sys/select.h>
int FD_ISSET(int fd, fd_set *fdset);
// fd 가 집합에 있을 경우의 반환 값 : 0 이 아닌 값
// fd 가 집합에 없을 경우의 반환 값 : 0
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
//
// FD_ZERO : 주어진 fd_set의 모든 비트를 0으로 초기화, select() 함수를 호출하기 전에 매번 FD_ZERO를 불러야함
// FD_SET : 집합의 특정 비트를 켤 때 사용, fd 를 집합에서 추가할 때 사용
// FD_CLR : 집합의 특정 비트를 끌 때 사용, fd 를 집합에서 제거할 때 사용
// FD_ISSET : 특정 비트가 켜져 있는지 확인 할 경우 사용, select() 호출이 반환된 후 fd 가 입출력 준비가 끝났는지 점검하기 위해 쓰임
//
// readfds, writefds, exceptfds 집합에는 감시할 fd 가 없으면 NULL 을 넣을 수 있고,
// 세 집합 모두 NULL 로 하면 sleep 보다 해상도가 높은 타이머를 사용할 수 있게 됨
// sleep 함수는 초 단위 시간만 대기 할 수 있지만 select 함수는 마이크로 초 단위까지 대기할 수 있기 때문
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500;
select(0, NULL, NULL, NULL, &tv);
// ===========================================================================================================
// mafdl : 점검할 fd의 갯수, fd 집합에서 가장 높은 fd 번호에 1을 더한 값을 넣어줘야함
// 최대 fd 번호에 1을 더하는 이유는 fd 번호가 0부터 시작하기 때문
// <sys/select.h>에 가장 큰 fd 번호로 FD_SETSIZE가 정의되어 있음
// 보통 1024가 정의되어 있는데 이 값은 너무 크므로 최대 fd 번호에 1을 더한 값을 넘겨주는게 좋음
// mafdl 인자가 클수록 select 함수가 감시할 fd가 많아지는 것이기 때문
// ===========================================================================================================
// ex
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define buf_length 1024
int main()
{
struct timeval tv;
fd_set readfds, tmpfds;
int select_result;
int read_result;
char buf[buf_length + 1];
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
while(1)
{
tmpfds = readfds;
tv.tv_sec = 3;
tv.tv_usec = 0;
select_result = select(STDIN_FILENO + 1, &tmpfds, NULL, NULL,&tv);
if (select_result == -1)
{
perror("select");
}
else if (select_result == 0)
{
printf("3 sec\n");
}
else
{
if (FD_ISSET(STDIN_FILENO, &readfds)
{
read_result = read(STDIN_FILENO, buf, buf_length);
if (read_result == -1) perror("read"), return(1);
if (read_result) buf[read_result] = '\0', printf("read : %s\n",buf);
if (strncmp(buf, "quit",4) == 0) break;
}
}// end of if
}// end of while
}// end of main
'C언어' 카테고리의 다른 글
리눅스(우분투 10.10) 공유 메모리 예제 2 (0) | 2013.11.07 |
---|---|
리눅스(우분투 10.10) 공유 메모리 예제 (0) | 2013.11.07 |
bind 에러 해결 방법 (0) | 2013.10.31 |
c++ 공부 요점정리 1 (0) | 2013.10.26 |
C#에서 printf 대신 출력 방법 (0) | 2013.10.25 |