epoll 은 감시해야 하는 fd가 많을 때 select나 poll에 비해 성능이 좋다.
epoll_create() epoll 인스턴스를 생성, 이 인스턴스를 참조하는 fd를 리턴한다.
epoll_ctl() 관심 목록 설정할때 사용. 새 fd를 추가 하거나 기존 목록에서 제고 할 수 있고 이벤트 감시를 변경 할 수 있다.
epoll_wait() 인스턴스에서 준비 목록을 리턴한다.
#include "sys/epoll.h" int epoll_create( int size ); // 성공하면 fd 리턴, 에러가 발생하면 -1 // 리눅스 2.6.8이전에는 커널에게 초기 데이터 구조체를 어떻게 설정하는지에 대한 거였다는데..... 이제는 필요 없단다 int epoll_create1() // 대신 이게 새로 생김 int epoll_ctl( int efd, int op, int fd, struct epoll_event* ev ); // 성공하면 fd 리턴, 에러가 발생하면 -1 // fd에는 pipe, pifo, socket, posix message queue, inotify instanc, divice, 다른 epoll 디스크립터등..이 될 수 있다. // 만약 안되는놈을 지정하면 EPERM 에러를 발생 한다. struct epoll_event { uint32_t events; epoll_data_t data; } typedef union epoll_data { void* ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; /** /proc/sys/fs/epoll 디렉토리에 있는 max_user_watches 에서 한도값을 확인 하고 수정 할 수 있다.**/
op 인자에 추가되는 flag
EPOLL_CTL_ADD |
fd 를 epfd의 관심 목록에 추가, 이미 목록에 존재한다면 EEXIST 에러를 발생 시킨다. event 집합은 *ev에 저장 된다. |
EPOLL_CTL_MOD |
*ev에 지정된 정보를 이용해 fd 설정 변경. 관심 목록에 없는 fd라면 ENOENT 에러를 발생 시킨다. |
EPOLL_CTL_DEL |
epfd 에서 fd를 제공 한다. epfd 관심 목록에 없는 fd를 제거하려면 ENOENT 에러를 발생 한다. fd를 닫으면 epoll 관심 목록에서 자동 제거 된다. |
#include "sys/epoll.h" int epoll_wait( int epfd, struct epoll_event* evlist, int maxevents, int timeout ); // 성공하면 준비된 fd count를 리턴 한다. // 타임 아웃이면 0, 에러면 -1을 리턴 // 준비된 fd는.. evlist가 가리키는 epoll_event구조체 배열로 리턴됨. // timeout 이 -1이면 epfd의 관심 목록에 있는 fd중 하나에서 이벤트나 시그널이 발생할때까지 블럭 // timeout 이 0이면 epfd에서 현재 이용 할 수 있는 fd가 있는지를 비블로킹 방식으로 검사 // timeout이 0보다 크면, epfd중 fd하나에서 이벤트나 시그널이 발생할때까지 timeout 밀리초만큼 블록 한다. //** 멀티쓰레드에서 한 쓰레드는 epoll_wait 하고 있고 다른 스레드에서 epoll_ctl을 수행해 epfd에 add할 경우, **// //** 새로 추가된 fd가 바로 적용된다. epoll_wait는 새로 추가된 fd의 준비 여부를 리턴 결과에 포함 한다. **//
비트 |
설명 |
EPOLLIN |
높은 우선순위 데이터 외의 데이터를 읽을 수 있다. |
EPOLLPRI |
높은 우선순위 데이터를 읽을 수 있다 |
EPOLLRDHUP | 상대편 소켓 셧다운 |
EPOLLOUT | 일반 데이터를 기록 할 수 있다 |
EPOLLET | 에지 트리거 이벤트 통지를 적용 |
EPOLLONESHOT |
이벤트 통지 뒤에 감시를 비활성화 한다. |
EPOLLERR | 에러 발생 |
EPOLLHUP | 장애 발생 (hangup) |
하늘색은 EPOLL_CTL의 입력, WAIT 리턴 둘다 사용
주황색은 CTL의 입력에서만 사용
회색은 WAIT 리턴에서만 사용
EPOLLONESHOT
기본적으론 epoll_ctl로 관심목록에 추가하면 제거할때까지 활성 상태로 남게 된다.
특정 fd로부터 통지를 받으려면 ev.ents값에 EPOLLONESHOT 를 지정하면 됨. 지정하고 wait를 호출하며 해당 fd가 ready상태인지 알려준뒤 해당 fd를 비활성화가 되고 이후에 다시 wait를 호출하면 비활성화된 fd는 안알려준다. (필요할 경우 EPOLL_CTL_MOD 를 사용해 재활성화 해야 함. )
#include "sys/epoll.h" void epoll( const char* pName ) { int epCnt = 10; epfd = epoll_create( epCnt ); if ( -1 == epfd ) return; for ( int i = 0; i < 5; ++i ) { int fd = open( pName, O_RDONLY ); if ( -1 == fd ) return; printf("Opened : %s, fd %d\n", pName, fd ); struct epoll_event event; event.events = EPOLLIN; ev.data.fd = fd; if ( -1 == epoll_ctl( epfd, EPOLL_CTL_ADD, fd, &event ) ) return; while ( epCnt - 1 > 0 ) { printf("About to epoll_wait\n"); // 한번에 처리 가능한 이벤트 갯수 const int MAX_EVENTS = 5; struct epoll_event eventList[ MAX_EVENTS ]; int ready = epoll_wait( epfd, MAX_EVENTS, -1 ); if ( -1 == ready ) { if ( EINTR == errno ) continue; // 시그널에 의해 중단되면 실행 재개 else return; // 준비된 fd 없음.. } printf("Ready : %d\n", ready ); for ( int eventIndex = 0; eventIndex < ready; ++eventIndex ) { printf("fd %d, EPOLLIN? %s, EPOLLHUP? %s, EPOLLERR? %s\n", eventList[ eventIndex ].data.fd, eventList[ eventIndex ].events & EPOLLIN ? "true" : "false", eventList[ eventIndex ].events & EPOLLHUP ? "true" : "false", eventList[ eventIndex ].events & EPOLLERR ? "true" : "false" ); } if ( eventList[ eventIndex ].events & EPOLLIN ) { int s = read( eventList[ eventIndex ].data.fd, buf, 1024 ); if ( -1 == s ) return; // 읽기 에러 printf("reaad %d bytes : %s\n", s, buf ); } else { printf("closing fd %d\n", eventList[ eventIndex ].data.fd ); if ( -1 == close( eventList[ eventIndex ].data.fd )) return; --epCnt; } } } printf("All file descriptors closed; bye\n"); }
'프로그래밍 > 리눅스' 카테고리의 다른 글
리눅스 기본 명령어들... (0) | 2017.04.29 |
---|---|
ssh 접속하기 (2) | 2017.04.28 |
I/O - poll (0) | 2013.03.25 |
I/O - select (0) | 2013.03.24 |
I/O 모델 종류.. (0) | 2013.03.24 |