select는 오래된 멀티플렉싱 기법이다.
epoll이 주로 활용된다.
select가 호출되면 변화가 있는 파일 디스크립터만 묶이는 것이 아니라 fd_set의 변화를 통해 변화한 파일디스크립터를 구분하기때문에 반복문은 필수적이다.
select 호출시 fd_set에 변화가 생기므로 select를 호출 전에 fd_set의 원본을 복사하고 계속 select 호출 시 새롭게 관찰 대상의 정보를 줘야한다
매번 관찰대상의 정보를 줘야한다??
이는 select 호출 시 정보를 운영체제에 전달한다는 것을 의미한다
운영체제에 정보를 전달하는 것은 오버로드가 크다.
소켓은 운영체제에 의해 관리된다.
select은 소켓의 변화를 관찰하는 함수이다.
그래서 select은 운영체제에 의해 완성되는 함수이다. 매번 정보를 운영체제에 전달해야한다.
=> 운영체제에 관찰대상의 정보를 한번만 알려주는 방법은?? epoll!!
epoll은 현재 관찰되는 파일 디스크립터와 관련 이벤트 정보를 운영체제에 보관하고 추가, 삭제, 수정할 수 있다
select
epoll
epoll은??
epoll_create epoll 파일 디스크립터 저장소 생성
epoll_ctl 저장소에 파일 디스크립터 등록 및 삭제
epoll_wait 파일 디스크립터의 변화를 대기
select에선 fd_set을 직접 선언했다.
하지만 epoll에선 운영체제가 한다. epoll_create로 운영체제에게 요청하면 된다.
select에선 FD_SET, FD_CLR로 파일디스크립터를 추가, 삭제한다.
epoll에선 epoll_ctl로 운영체제에게 요청하는 형식으로 이뤄진다.
select에선 select함수를 호출해 변화를 대기하지만,
epoll에선 epoll_wait을 사용한다.
select에선 fd_set의 변화를 통해 상태변화를 확인하지만,
epoll에선 epll_event 구조체를 기반으로 상태변화가(이벤트) 발생한 파일디스크립터가 별도로
묶인다.
struct epoll_event{
__uint32_t events;
epoll_data_t data;
}
이 구조체를 배열로 만들어 epoll_wait에 인자로 전달하면, 상태변화가 발생한 파일 디스크립터의 정보가 배열에 별도로 묶여, 반복문이 불필요
int epoll_create(int size);
epoll 인스턴스 생성 후 관찰대상이 되는 파일 디스크립터를 등록해야함.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
EX로 알아보기
epoll_ctl(A,EPOLL_CTL_AOD,B,C)
epoll_ctl(A,EPOLL_CTL_DEL,B,NULL)
EPOLL_CTL_ADD 등록
EPOLL_CTL_DEL 삭제
EPOLL_CTL_MOD 이벤트 발생상황 변경
epoll_event 구조체는 이벤트가 발생한 파일 디스크립터를 묶는 용도
로 사용된다.
또한, 파일디스크립터를 epoll 인스턴스에 등록
할 때, 이벤트의 유형을 등록
하는 용도로도 사용
struct epoll_event event;
...
event.evetns = EPOLLIN;
event.data.fd= sockfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);
epfd(epoll 인스턴스)에 sockfd등록, 수신 데이터 존재 시 이벤트 발생하게 등록
이벤트 유형
엣지 트리거
에서 유용이벤트들은 or연산자를 통해 둘 이상을 함께 등록 가능하다
보통 epoll 관련 함수 중 마지막으로 호출 됨
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
두번째 인자로 전달되는 주소값의 버프를 동적으로 할당!
해당 함수 호출 후엔 이벤트가 발생한 fd의 수가 반환, 두번째 인자에 fd가 별도로 묶이기 때문에 select와 달리 전체 fd반복문 필요 없다.
이벤트가 발생하는 시점에 있다.
계속해서
이벤트가 등록 됨한번만
이벤트가 등록 됨select 모델은 레벨트리거 방식으로 동작
설정해야하는 것이 2가지 존재
errno를 이용한 오류의 원인을 확인
Non-blocking 모드일때 입력이 완료된것을 알아야할 필요가 있다
데이터가 수신되면 딱 한번 이벤트가 등록된다
이벤트가 발생하면 버퍼에 데이터를 모두 읽어야한다
read는 더이상 읽을 데이터가 없을 때 -1을 반환하고 errno 값은 EAGAIN이 저장되면 더이상 읽을 데이터가 없는 상황이다
Non-blocking IO를 위한 소켓의 특성을 변경
int flag = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flag|O_NONBLOCK);
엣지트리거
방식을 사용하면
ex
서버에서, 관리해야하는 데이터의 양이 커지면 엣지트리거가 성능이 좋다
왜?? 이벤트를 구분할 수 있으므로
만약 관리해야하는 데이터가 간단하고 다양하지 않다면 레벨 트리거가 성능이 더 좋다