본문 바로가기

프로그래밍/ 리눅스

I/O - epoll

 

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