다중 입출력
은 여러 개의 파일에서 발생하는 입출력을 함께 관리하는 기술이다.select()
에서 지원하는 파일의 크기 기본값은 1024이다. 크기를 늘리려면 매크로 FD를 수정해야 한다.select()
처럼 표준 입출력 에러를 따로 감시할 필요가 없다.Select()
allows a program to monitor file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g. input possible). A dile descriptor is considered ready if it is possible to perform a corresponding I/O operation (e.g. read() or a sufficiently small write() without blocking. Select()
can monitor only file descriptors numbers that are less than FD_SETSIZE; Poll()
does not have this limitation.
Poll()
performs a similar task to select()
: it waits for one of a set of file descriptors to become ready to perform I/O. The set of file descriptors to be monitored is specified in the fds argument..: struct pollfd.
Epoll()
performs a similar task to poll()
: monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll()
can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watching file descriptors.
Epoll()
을 이용한 기법에서는, 관찰해야할 파일 디스크립터와, 그 디스크립터로부터 관찰해야할 event의 종류를 'epoll 인스턴스'에 저장한다. 이 'epoll 인스턴스'에 등록된 파일 디스크립터들은 운영체제에 넘겨져 저장되며, 이후에 프로그램이 epoll_wait() 함수를 호출해 대기하고 있으면 등록된 event가 발생할 때 운영체제가 해당 파일 디스크립터를 프로세스로 넘겨준다. Select()
기법에서 직접 파일 디스크립터 배열을 검사하며 event를 찾아내야 했던 것과는 다르게, epoll 기법에서는 모든 계획을 운영체제에 미리 전달한 뒤 epoll_wait 함수를 통해 기다리기만 하면 되는 것이다.
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
설명 파일 디스크립터의 변화를 확인한다.
fd_set을 전달하여 호출하면 변화가 발생한(입력 받은 데이터가 존재하거나, 출력이 가능한 상황 등) 소켓의 디스크립터만 1로 설정한다.
fd_set에 대한 주소값을 전달하고 각 액션에 대한 결과를 적용하기 때문에 원본을 복사하여 복사본을 전달해야 한다.
기본적으로 blocking함수이다. (확인할 파일 디스크립터에 변화가 생길 때까지 무한 대기)
fd가 입출력을 수행할 준비가 되거나 정해진 시간이 경과할 때까지 block된다.
매개변수1 int nfds
감시할 fds의 갯수
매개변수2 fd_set* readfds
읽을 데이터가 있는지 검사하기 위한 파일 목록
/* fd를 관리하기 위해 디자인된 구조체 */
/* 배열 형태로 0번 인덱스부터 fd 0을 매핑하고 있다. */
typedef struct fd_set {
u_int fd_count; /* 설정하는 소켓의 번호 */
SOCKET fd_array[FD_SETSIZE] /* 설정된 소켓의 배열 */
} fd_set;
매개변수3 fd_set* writefds
쓰여진 데이터가 있는지 검사하기 위한 파일 목록
매개변수4 fd_set* exceptfds
파일에 예외 사항들이 있는지 검사하기 위한 파일 목록
매개변수5 struct timeval* timeout
데이터 변화를 감시하기 위해서 사용하는 time-out. NULL이면 계속 대기한다.
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
}
tv_sec가 3이고 tv_usec가 500,000이면, 타임아웃은 3.5초로 설정된다.
설정한 timeval 구조체 변수의 포인터를 timeout 인자로 넘겨주면, 파일 디스크립터에 아무런 변화가 없더라도 3.5초가 지나면 무조건 리턴한다. 만약 타임아웃을 설정하지 않으면, NULL포인터를 인자로 전달하면 된다.
return
1. 성공 int
변화가 발생한 파일 디스크립터의 갯수
(예시) 수신할 데이터가 존재하는 파일 디스크립터가 두개 발생했다면, 리턴값은 2이다.
2. 타임아웃 0
파일 디스크립터에 변화가 없음
2. 실패 -1 errno
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void FD_CLR(int fd, fd_set* set);
설명 매개변수 set으로 전달된 주소의 변수에서, 매개변수로 전달된 파일 디스크립터 정보를 삭제한다.
매개변수1 int fd
매개변수2 fd_set* set
typedef struct fd_set {
u_int fd_count; /* 설정하는 소켓의 번호 */
SOCKET fd_array[FD_SETSIZE] /* 설정된 소켓의 배열 */
} fd_set;
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void FD_ISSET(int fd, fd_set* set);
설명 매개변수 set으로 전달된 주소의 변수에, 매개변수 fd로 전달될 파일 디스크립터 정보가 있으면 양수를 반환한다.
매개변수1 int fd
매개변수2 fd_set* set
typedef struct fd_set {
u_int fd_count; /* 설정하는 소켓의 번호 */
SOCKET fd_array[FD_SETSIZE] /* 설정된 소켓의 배열 */
} fd_set;
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void FD_SET(int fd, fd_set* set);
설명 매개변수 set으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보를 등록한다.
매개변수1 int fd
매개변수2 fd_set* set
typedef struct fd_set {
u_int fd_count; /* 설정하는 소켓의 번호 */
SOCKET fd_array[FD_SETSIZE] /* 설정된 소켓의 배열 */
} fd_set;
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void FD_SET(fd_set* set);
설명 매개변수 set으로 전달된 주소의 변수의 모든 비트를 0으로 초기화한다.
매개변수1 fd_set* set
typedef struct fd_set {
u_int fd_count; /* 설정하는 소켓의 번호 */
SOCKET fd_array[FD_SETSIZE] /* 설정된 소켓의 배열 */
} fd_set;
#include <poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);
설명 감시할 fd의 번호와 조건을 담은 pollfd 구조체 배열을 nfds 갯수만큼 사용한다.
fds에 해당 events가 발생 하는지를 검사하고, 해당 events가 발생하면 revents를 채워 돌려준다. revents는 events가 발생했을 때 커널에서 이 events에 어떻게 반응했는지에 대한 반응값이다. (예: event가 제대로 처리되었으면 POLLIN, 에러가 발생하면 POLLERR)
매개변수1 struct pollfd* fds
struct pollfd {
int fd; //관심있어 하는 파일 디스크립터
short events; //발생된 이벤트
short revents; //돌려받은 이벤트
}
< events, revents는 아래의 값들을 가질 수 있다. >
(ㄴepoll_ctl 함수에 Events테이블 있음)
매개변수2 nfds_t nfds
unsigned int nfds
pollfd의 배열의 크기 (우리가 조사할 파일 디스크립터의 크기)
매개변수3 int timeout
1) 값을 지정하지 않을 경우 이벤트가 발생하기 전까지 영원히 기다린다.
2) 0일 경우, 기다리지 않고 곧바로 다음 루틴을 진행한다.
3) 0보다 큰 양의 정수일 경우, 해당 시간만큼을 기다린다. 해당 시간 내에 어떤 이벤트가 발생하면 즉시 되돌려주며, 시간을 초과한 경우 0을 리턴한다.
return
1. 성공 int
fd의 갯수
2. 타임아웃 0
3. 실패 -1 errno
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_create(int flags);
설명 커널 공간에 epoll 파일 디스크립터를 생성하는 함수
매개변수1 int size
epoll 인스턴스의 크기 정보
리눅스 2.6.8 이후 size는 무시되기 때문에 0 이상의 값으로만 넣으면 된다.
(이유: 커널 내에서 epoll 인스턴스 크기를 유동적으로 변화시키기 때문)
return
1. 성공 int
자신의 다른 함수에서 사용할 새로운 파일 디스크립터를 리턴
2. 실패 -1 errno
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
설명 epoll에 관찰 대상인 파일 디스크립터를 등록하는 함수.
매개변수1 int epfd
관찰대상으로 등록할 epoll의 fd 값
매개변수2 int op
관찰대상의 추가, 삭제 또는 변경여부 지정 옵션
EPOLL_CTL_ADD
관심있는 파일 디스크립터를 추가. 이미 존재하면 EEXIST 에러 발생EPOLL_CTL_MOD
기존 파일 디스크립터를 수정. 목록에 없으면 ENOENT 에러 발생EPOLL_CTL_DEL
기존 파일 디스크립터를 관심 목록에서 삭제. 목록에 없으면 ENOENT 에러 발생매개변수3 int fd
대상 파일 디스크립터
매개변수4 struct epoll_event* event
관찰대상의 관찰 이벤트 유형
주의할 점은 여기서 event는 '어떤 이벤트를 확인할 것인가'에 대한 이벤트 구조체 변수의 주소이다.
따라서 변화가 생긴 fd를 저장하는 events 구조체는 나중에 따로 선언해야 한다.
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* 사용자 데이터 */
}
typedef union epoll_data {
void *ptr; /* 이벤트가 발생한 대상 객체를 가리킨다. */
int fd; /* 파일 디스크립터 */
uint32_t u32; /* 32비트 정수 */
uint64_t u64; /* 64비트 정수 */
} epoll_data_t;
struct epoll_event tEvent; //변수 선언
tEvent.events = EPOLLIN; //event멤버에 원하는 것 등록 (비트 || 연산자를 사용해 둘 이상을 함께 등록 가능)
tEvent.data.fd = nListenFd; //파일 디스크립터 설정
Events | 설명 |
---|---|
EPOLLIN | 수신할 데이터가 존재하는 상황 (epoll_ctl의 입력, wait의 출력에 모두 사용됨) |
EPOLLOUT | 출력 버퍼가 비워져서 당장 데이터를 전송할 수 있는 상황 (epoll_ctl의 입력, wait의 출력에 모두 사용됨) |
EPOLLRDHUP | 연결이 종료되거나 Half-close가 진행 상황. 이는 엣지 트리거 방식에서 유용하게 사용될 수 있음. 상대편 소켓 셧다운 (epoll_ctl의 입력, wait의 출력에 모두 사용됨) |
EPOLLPRI | 중요한 데이터(OOB)가 수신된 상황 (epoll_ctl의 입력, wait의 출력에 모두 사용됨) |
EPOLLERR | 에러가 발생한 상황 (epoll_wait의 출력으로만 사용됨) |
EPOLLHUP | 연계 파일 디스크립터에서 연결이 끊김 및 장애가 발생함 (epoll_wait의 출력으로만 사용됨) |
EPOLLET | 이벤트의 감지를 엣지 트리거 방식으로 동작(기본은 레벨 트리거 방식) (epoll_ctl의 입력에만 사용됨) |
EPOLLONESHOT | 이벤트가 한 번 감지되면, 해당 파일 디스크립터에서는 더 이상 이벤트를 발생시키지 않음. 따라서 epoll_ctl 함수의 두번째 인자로 EPOLL_CTL_MOD를 전달해서 이벤트를 재설정해야함. (epoll_ctl의 입력에만 사용됨) |
return
1. 성공 0
2. 실패 -1 errno
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
설명 등록한 파일 디스크립터의 변화를 탐지한다.
매개변수1 int epfd
epoll_create()로 반환된 epollFd
매개변수2 struct epoll_event* events
이벤트가 발생한 fd가 채워질 버퍼의 주소값
실제 동시 접속 수와 상관없이 최대 몇 개까지의 event만 처리할 것임을 지정해주도록 함.
epoll_event의 주소값으로 이벤트가 발생했을 때 그 fd가 담길 버퍼!!
epoll_event* 자체를 변수로 선언하고 동적할당 해주어야 한다.
매개변수3 int maxevents
events 버퍼에 최대 몇 개의 이벤트를 담을건지
매개변수4 int timeout
1/1000초 단위의 대기시간. (1밀리세컨드 단위). -1
전달시 이벤트가 발생할 때 까지 무한대기 상태가 되고, 0
이면 즉시 조사만 하고 리턴한다.
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* 사용자 데이터 */
}
typedef union epoll_data {
void *ptr; /* 이벤트가 발생한 대상 객체를 가리킨다. */
int fd; /* 파일 디스크립터 */
uint32_t u32; /* 32비트 정수 */
uint64_t u64; /* 64비트 정수 */
} epoll_data_t;
struct epoll_event* ptEvents;
ptEvents = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);
return
1. 성공 int
이벤트가 발생한 fd 갯수
2. 타임아웃 0
3. 실패 -1 errno