Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

Code Habit

epoll 본문

카테고리 없음

epoll

코드베어 2024. 2. 9. 11:23

epoll은 리눅스에서 고성능 네트워크 프로그래밍을 위해 사용되는 I/O 이벤트 알림 모델이다. 대규모 파일 디스크럽터를 효율적으로 관리하며 이는 서버가 많은 수의 클라이언트와 동시에 통신해야 하는 고성능 네트워크 애플리케이션에 매우 유용하다.

 

- epoll_create

int epoll_create(int size);

size만큼의 입출력 이벤트를 저장할 공간을 만든다. 그러나 리눅스 2.6.8 부터는 커널이 필요한 데이터의 크기를 동적으로 조정하기 때문에 0보다 큰 값만 입력하면 된다.

 

- epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_events *event);

epoll에 fd를 등록/수정/삭제 하는 함수로 epoll에서 관심있는 fd와 이 fd에서 발생할 event를 등록할 수 있다.

op에 fd의 등록/변경/삭제를 결정하는 옵션들을 지정할 수 있다. ( EPOLL_CTL_ADD / EPOLL_CTL_MOD / EPPOL_CTL_DEL )

 

 

- epoll_wait

int epoll_wait(int epfd, struct epoll_evnet *events, int maxevents, int timeout);

관심있는 fd들에게 무슨 일이 있었는지 검사한다. 검출할 사건들의 리스트를 events->events[]로 전달하고 이벤트가 발생하면 발생한 이벤트 수를 리턴받는다. maxevents로 한번에 전달 받을 이벤트의 최대값을 지정할 수 있고 timeout으로 이벤트를 기다리는 시간을 밀리세컨드 단위로 전달할 수 있는데 -1을 전달하면 계속해서 이벤트를 기다리는 blocking 상태고 되고 0을 전달하면 이벤트 유무만 검사하고 즉시 리턴하는 상태가 된다.

 

- example

#include<cstdio>
#include <iostream>
#include <string.h>
#include <fcntl.h>

using namespace std;

#include <sys/unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>

#define SERVER_PORT 7001
#define BACKLOG 10
#define MAX_EVENTS 1000;

void setnonblocking(int sock)
{
    int flag = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, flag | O_NONBLOCK);
}

int setup_socket()
{
    int sock;
    struct sockaddr_in sin;

    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }

    memset(&sin, 0, sizeof sin);

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(SERVER_PORT);

    if (bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0)
    {
        close(sock);
        return -1;
    }

    if (listen(sock, BACKLOG) < 0)
    {
        close(sock);
        return -1;
    }

    return sock;
}

int main()
{
    int server_fd = 0;
    int epoll_fd = 0;
    struct epoll_event event;
    struct epoll_event events[MAX_EVENTS];
    int event_count = 0;
    char buffer[1024] = {
        0,
    };

    // setup socket
    server_fd = setup_socket();
    if (server_fd < 0)
    {
        return 0;
    }

    // create epoll fd
    epoll_fd = epoll_create(100);
    if (epoll_fd < 0)
    {
        close(server_fd);
        return 0;
    }

    // set event
    memset(&event, 0, sizeof event);
    event.events = EPOLLIN | EPOLLHUP;
    event.data.fd = server_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0)
    {
        close(server_fd);
        close(epoll_fd);
        return 0;
    }

    while (true)
    {
        event_count = epoll_wait(epoll_fd, epoll_events, MAX_EVENTS, 100);
        for (int i = 0; i < event_count; i++)
        {
            // accept
            if (epoll_events[i].data.fd == server_fd)
            {
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof client_addr;

                int client_fd = accept(server_fd, (struct sockaddr_in *)&client_addr, (socklen_t *)&client_addr_len);
                if (client_fd < 0)
                {
                    continue;
                }

                // set client_fd non-block
                setnonblocking(client_fd);

                // set event
                memset(&event, 0, sizeof event);
                event.events = EPOLLIN | EPOLLHUP;
                event.data.fd = client_fd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) < 0)
                {
                    close(client_fd);
                    continue;
                }
            }
            else
            {
                int client_fd = events[i].data.fd;
                int n = read(client_fd, buffer, buffer_fd);
                if (n < 0)
                { // error
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);
                    close(client_fd);
                }
                else if (n == 0)
                { // client close
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);
                    close(client_fd);
                }
                else
                { // read data
                    write(client_fd, buffer, n); // write echo message
                }
            }
        }
    }

    return 0;
}

server socket을 셋팅하고 epoll에 등록하여 client 접속 및 메시지를 처리하는 예제이다. 메시지를 받으면 받은 메시지를 바로 write하여 간단한 echo 역할을 수행한다.

 

참고 ) https://jacking75.github.io/choiheungbae/문서/epoll을%20사용한%20비동기%20프로그래밍.pdf