KqueueProgramming

kqueue 관련 함수들

kqueue를 사용하려면 밑의 해더를 추가해야 합니다.

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>

int kqueue( void );
int kevent( int kq, // [in] kqueue() 함수가 반환한 kqueue descriptor
            const struct kevent * changelist, // [in] kqueue event queue에 등록할 이벤트 구조체
            int nchanges, // [in] 등록할 이벤트의 개수
        struct kevent * eventlist, // [out] 이벤트 발생시 들어오는 이벤트 배열
            int nevents, // [in] 들어오는 이벤트의 최대값
        const struct timespec * timeout ); // [in] 기다리는 시간

리턴값
* error가 나면 -1을 리턴하고, errno 를 설정한다.

인자
* kq : kqueue의 파일 기술자 (kqueue() 함수의 리턴값)

설명
큐에 이벤트들을 등록하거나, 미처리된 이벤트들(pending events)을 사용자에게 반환하고자 할 때 동시에 사용된다.
큐에 이벤트들을 등록하는 것은 changelist, nchanges를 통해서 일어난다. 달리 말해서 어떤 이벤트(event source의 event types; Read, Write, Add, Delete)를 시스템이 감지하게 할 것인지를 요청하는 것이다. 이 작업은 kevent() 를 매번 호출 하는 것이 아니라, 변화가 있을 경우에만 호출한다. 예를 들어 어떤 이벤트 소스(이벤트를 발생시키는 자원)에 대해서 EV_ADD와 EVFILT_READ를 요청하기 위해 changelist에 정보를 담아서 kevent()를 호출한 경우에는 다음에 kevent()를 호출시엔 changelist에 또 다시 위의 정보를 담지 않아도 된다는 뜻이다.
이 함수를 통해서 미처리된 이벤트들을 얻고자 할 경우엔 이 함수로부터 복귀후 eventlist, nevents를 통해 그 정보를 알 수가 있다.
Unix의 Man 페이지에선 changelist와 eventlist는 통상적으로 같은 배열을 사용한다고 되어 있지만, 좀더 안전하게 사용하기 위해선 위 둘을 따로 마련해 두는 것이 여러모로 편리하다.
추가되거나 변경되는 이벤트를 등록하고 발생한 이벤트를 eventlist에 넣고 발생한 이벤트의 수를 리턴한다. 이벤트 등록시 중복되는 이벤트를 등록하면 안된다. 따라서 다시 kevent()함수를 호출시에는 nchanges 값은 초기화 되어야 한다.

사용법
전형적으로 kevent() 호출은 반복해서 일어난다. 보통 kevent를 호출하고 복귀되면 nchanges는 0으로 설정해야한다. 전형적으로 다음 호출시에 이벤트 등록과 관련한 정보를 초기화 하는 작업인 것이다. 만일 이벤트 등록이 필요한 경우에는 changelist와 nchanges의 값이 kevent()가 호출되기 이전에 변화되어야 할 것이다.
그리고 이벤트 등록이 필요한 경우에는 한 객체가 changelist와 nchanges의 값을 변경하는데 책임을 지는 것이 효율적이다.

EV_SET( &amp;kev, ident, filter, flags, fflags, data, udata );

메크로를 사용하지 않고 다음과 같은 함수를 직접 만들어 사용할 수 있습니다.

void ChangeEvent( int nIdent, int nFilter, int nFlags, void * pUdata )
{
    ChangeList[nChanges].ident = nIdent;
    ChangeList[nChanges].filter = nFilter;
    ChangeList[nChanges].flags = nFlags;
    ChangeList[nChanges].fflags = 0;
    ChangeList[nChanges].data = 0;
    ChangeList[nChanges].udata = pUdata;
    nChanges++;
}
struct kevent
{
    uintptr_t ident; /* 이벤트에 대한 identifier(파일 기술자) */
    short filter;    /* 이벤트 필터 플래그 */
    u_short flags;   /* kqueue?대한 액션 플래그 */
    u_int fflags;    /* 필터 플래그 값 */
    intptr_t data;   /* 필터 데이터 값 */
    void * udata;    /* 사용자 정의 데이터 */
};

flags
Input flags
* EV_ADD : 이벤트를 추가한다.

Output flags
* EV_EOF :

fflags
* EVFILT_READ :

대략적인 사용법

/****************************************
// 1. kqueue를 초기화 및 데이터 설정
****************************************/
int nKqueue;
nKqueue = kqueue();

struct kevent ChangeList[MAX_CHANGE];
nChanges = 0;

struct kevent EventList[MAX_EVENT];

/****************************************
// 2. 알고자하는 READ/WRITE 이벤트를 준비
****************************************/

// 예를 들어 READ 이벤트를 잡고 싶다면
// filter =  EVFILE_READ, flags = EV_ADD | EV_ENABLE
// 삭제시에는 EV_ADD 대신 EV_DELETE를 주고 호출
ChangeEvent( socket, EVFILT_READ, EV_ADD | EV_ENABLE, pData );

/****************************************
// 3. 준비된 이벤트를 커널에 등록
****************************************/

// 타임아웃 설정
struct timespec timeout = { 0, 0 };

int nEventCount = 0;
nEventCount = kevent( nKqueue, ChangeList, nChanges, EventList, MAX_EVENT, &amp;timeout );
// 등록할 이벤트는 등록되었으므로 다시 초기화 시킨다.
nChanges = 0;

/****************************************
// 4. 에러 처리
****************************************/

if( nEventCount == -1 )
{
    // error
}
if( nEventCount == 0 )
{
    // timeout
}

/****************************************
// 5. 돌아온 이벤트의 종류를 보고 적당히 처리
****************************************/

for( int i = 0; i < nEventCount; i++ )
{
    if( EventList[i].flags &amp; EV_ERROR )
    {
        if( EventList[i].data == EBADF || m_pEventList[i].data == ENOENT )
            // 이게 무슨 경우였는지 생각이 안나네요. -_-;
        else
            // 이 경우도... 아시는 분 수정해주세요.
    }

    if( m_pEventList[i].filter == EVFILT_READ )
    {
        // 사용자로부터 패킷을 받았다.
        ( ( CUser * ) m_pEventList[i].udata )->OnRead();
    }
}

/****************************************
// 6. 사용이 끝나면 kqueue를 닫는다.
****************************************/

close( nKqueue );

관련링크