Reactive Programmming

select, epoll

재심 2025. 1. 12. 21:49

목차

    select와 epoll의 개념을 이해하기 위해 FD 관련된 내용을 알아야 하므로 링크 참조.

    https://jaemni.tistory.com/entry/File-Descriptor-File-Table-Inode-Table

     

    File Descriptor, File Table, Inode Table

    [File Descriptor]프로세스에서 열린 파일 목록을 관리하는 테이블. (네트워크 소켓 역시 FD로 관리된다.)각 프로세스는 자신만의 FD를 가진다.프로세스가 파일에 접근할 때 FD를 사용하며 파일을 열면

    jaemni.tistory.com

     

    [개요]

    select와 epoll 모두 I/O 멀티플렉싱을 구현하기 위해 시스템콜.

     

    I/O 멀티플렉싱이란 하나의 프로세스가 여러 I/O 작업을 동시에 감시하고 처리할 수 있도록 하는 기법

     

    이를 통해 프로그램을 여러 개의 파일 디스크립터 (FD)를 동시에 감시하고, 하나 이상의 디스크립터가 읽기/쓰기 작업을 할 준비가 되었는지 확인할 수 있다.

     

    하지만 select와 epoll의 설계 철학과 성능은 다르다고 한다.

     

    [select]

    오래된 방식으로 모든 UNIX 계열 OS에서 지원

     

    작동 방식

    • 디스크립터 집합 준비:
      • 감시할 파일 디스크립터 집합을 준비
      • 읽기, 쓰기, 예외에 대한 집합(fd_set)을 각각 준비
    • 커널에 전달:
      • 파일 디스크립터 집합을 select() 시스템 호출로 전달
    • 상태 확인:
      • 커널은 감시 중인 파일 디스크립터들을 순차적으로 검사하여 준비된 디스크립터를 찾음
      • 순차적으로 검사하기 때문에 O(n) 의 시간 복잡도를 보인다.
    • 결과 반환:
      • 준비된 디스크립터를 다시 사용자 공간으로 반환하며, 애플리케이션은 반환된 정보를 처리

    특징

    • 파일 디스크립터 제한:
      • 기본적으로 감시할 수 있는 파일 디스크립터 수는 FD_SETSIZE(보통 1024)로 제한
    • 매번 디스크립터 재설정 필요:
      • select 호출 시마다 디스크립터 집합을 초기화하고 전달해야 한다.
    • 비효율적인 검사:
      • 커널이 모든 디스크립터를 순차적으로 확인하므로, 디스크립터 수가 많아지면 성능이 저하

    [epoll]

    epoll은 리눅스에서 select와 poll의 성능 문제를 해결하기 위해 도입된 I/O 다중화 방식.

    리눅스 2.6 이상에서 지원하며 OS가 fd세트를 관찰하다가 I/O가 준비된 fd가 있다면 알려준다.

    OS 자체가 fd세트를 직접 관찰하기 때문에 관심있는 fd 세트를 등록하기만 하면 OS가 알아서 체크해서 알려준다.

     

    리눅스는 epoll, macOS는 kqueue, 윈도우라면 IOCP라는 이름으로 비슷한 기능을 지원한다고 한다.

     

    epoll 함수

    • epoll_create: epoll 인스턴스를 생성한다. 관심목록과 준비목록을 포함한다
      • 관심목록: 감시하기 위해 등록된 fd 세트 (eg: 관심있는 fd와 대상 이벤트). 준비된 fd 세트들도 여기에 포함된다.
      • 준비목록: I/O 준비 상태인 fd세트. 이 값들은 동적으로 계속 변경된다.
    • epoll_ctl: epoll 인스턴스에 fd와 관심있는 작업을 등록/삭제/수정한다.
      • create 결과로 얻은 epfd를 파라미터로 전달하고, 관심있는 fd와 이벤트 종류를 전달한다.
      • 이벤트를 등록/삭제/수정 여부를 함께 전달한다
    • epoll_wait: fd와 관련된 이벤트를 감시한다.

    epoll_ctl의 이벤트 상수

    • EPOLLIN: fd의 read가 준비 완료
    • EPOLLOUT: fd의 write가 준비 완료
    • EPOLLRDHUP: socket이 상대방의 연결을 닫은 경우
    • EPOLLPRI: fd에 예외가 발생
    • EPOLLERR: fd에 오류가 발생
    • EPOLLET: 에지 트리거 알림을 요청

    작동 방식

    1. epoll 객체 생성:
      • epoll_create 또는 epoll_create1로 epoll 객체를 생성
    2. 디스크립터 등록:
      • epoll_ctl로 감시할 디스크립터를 커널에 등록
    3. 이벤트 대기:
      • epoll_wait를 호출하여, 이벤트가 발생한 디스크립터 목록을 반환

    특징

    • 파일 디스크립터 제한 없음
      • 디스크립터 수는 커널과 시스템 자원의 한계에 따라 결정되며, 사실상 제한이 없다고 한다.
    • 효율성:
      • 이벤트 기반으로 동작하므로, 준비되지 않은 디스크립터는 검사하지 않아 CPU 부하가 낮음
    • 디스크립터 유지:
      • 디스크립터가 한 번 등록되면 epoll 객체 내부에서 지속적으로 관리되므로, 매번 설정할 필요가 없음.

    epoll_wait

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

     

    epoll 인스턴스에서 이벤트를 대기하고 이벤트에 준비가 완료된 fd 목록을 최대 maxevents개 만큼 반환한다.

    timeout 시간동안 블록되며 -1로 지정하면 하나의 fd라도 준비될 때 까지 무한 대기

     

     

    [요약]

    특징 select epoll
    지원 파일 디스크립터 수 제한적 (기본 1024개, 설정 변경 필요) 사실상 무제한 (커널 및 시스템 자원에 따라 결정)
    디스크립터 관리 방식 매번 디스크립터 전체를 전달 이벤트 기반으로 디스크립터 등록 및 관리
    성능 디스크립터 수가 많아질수록 비효율적 디스크립터 수에 관계없이 효율적
    알림 방식 순차 검사 (Polling) 이벤트 발생 시 알림 (Callback-like)
    사용 인터페이스 간단하지만 디스크립터 갱신 필요 복잡하지만 디스크립터를 커널에서 지속적으로 관리