epoll 내부 동작 원리 및 select/poll 대비 비교

대규모 연결 환경에서의 I/O 처리 문제

5000개의 클라이언트 연결을 관리하는 채팅 서버에서 select 사용 시 CPU 사용률이 80%까지 상승하는 경우, 그 원인은:

  • 매 호출마다 전체 파일 디스크립터(fd)를 커널에 복사
  • 커널이 모든 fd를 순차적으로 점검(O(n) 복잡도)

poll은 fd 제한이 없지만 동일한 비효율 발생. epoll은 활성 연결만 효율적으로 처리하는 것이 핵심 목표입니다.

아키텍처 비교: 폴링 vs 콜백

방식작동 예시효율성
select모든 고객 집 방문 확인O(전체 연결 수)
poll용량 확장된 방문 확인O(전체 연결 수)
epoll수거함에 도착한 패킷만 확인O(활성 연결 수)

epoll은 등록-콜백 메커니즘으로 동작: fd를 한 번 등록하면 데이터 도착 시 커널이 직접 통보합니다.

epoll의 핵심 데이터 구조

struct eventpoll {
    struct rb_root  tree_root;   // 등록된 fd 저장 (레드-블랙 트리)
    struct list_head ready_list; // 데이터 도착 fd 연결 리스트
    wait_queue_head_t wait_q;    // 대기 프로세스 큐
};

동작 흐름:

  1. 등록(epoll_ctl): fd를 트리에 삽입, 소켓에 콜백 함수 연결
  2. 데이터 수신: 네트워크 스택이 ep_poll_callback 실행 → ready_list에 fd 추가
  3. 이벤트 처리(epoll_wait): ready_list에서 활성 fd만 추출(O(활성 연결))

성능 비교표

항목selectpollepoll
fd 한계1024무제한무제한
시간 복잡도O(n)O(n)O(활성 연결)
커널 복사매번 전체 복사매번 전체 복사1회 등록

에지 트리거(ET) vs 레벨 트리거(LT)

LT (기본)ET
버퍼에 데이터 존재 시 지속 알림데이터 도착 시점에 1회 알림
부분 읽기 허용반드시 EAGAIN까지 전체 읽기
안정성 높음고성능 요구 환경 적합

epoll 서버 구현 예제

// 핵심 프레임워크
int epoll_fd = epoll_create(0);
struct epoll_event reg_event;
reg_event.events = EPOLLIN; // LT 모드
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, ®_event);

struct epoll_event active_events[MAX_EV];
int cnt = epoll_wait(epoll_fd, active_events, MAX_EV, -1);

// ET 모드 데이터 처리
while((len = read(fd, buf, BUF_SIZE)) {
  if (len == -1 && errno == EAGAIN) break;
  // 데이터 처리
}

ET 모드 주의사항

  • 반드시 넌블로킹 모드 설정: fcntl(fd, F_SETFL, O_NONBLOCK);
  • 데이터 완전 읽기: EAGAIN 에러 반환 시까지 읽기 반복

핵심 동작 요약

  • 빠른 처리가 가능한 이유: 등록된 fd에 대한 이벤트 기반 알림
  • 레드-블랙 트리: 효율적인 fd 관리(O(log n))
  • ET 모드: 높은 처리량 보장 but 구현 복잡도 증가

태그: epoll select poll 리눅스-네트워킹 IO-멀티플렉싱

6월 24일 02:38에 게시됨