I/O Multiplexing (select, poll, epoll, IOCP, kqueue)
What is I/O Multiplexing?
I/O Multiplexing은 한 프로세스가 동시에 여러 파일 디스크립터를 관리하는 기법이다. 프로그램은 파일 디스크립터를 모니터링하여 어떤 종류의 I/O 이벤트(읽기, 쓰기, 예외 등)가 발생했는지 확인하고, 각각의 파일 디스크립터가 Ready 상태가 되었는지 판단한다. 이 기법은 Network socket과 같은 비동기적 I/O 작업에 특히 유용하며, socket도 IP와 Port로 구성된 파일로 취급되기 때문에 Socket Programming에서도 효과적으로 사용될 수 있다. I/O Multiplexing을 구현하기 위한 방법으로는 select, poll, epoll 등이 있다.
I/O Multiplexing Methods
select
Single 스레드에서 여러 파일을 처리할 때 사용된다.
최대 1024개의 file descriptor를 저장하는 배열을 사용하며, sequential search 방식으로 대상 file descriptor를 탐색하기 때문에, file descriptor 개수가 많아질수록 성능이 저하된다.
오래된 시스템에서도 호환되지만, 효율성이 낮아 현대적 요구에는 부적합하다.
poll
관리 가능한 최대 FD 수가 1024로 제한적이었던 select와 달리 무한 개의 file descriptor를 검사할 수 있다.
sequential search 방식을 사용하는 건 여전하므로 select와 마찬가지로 file descriptor 개수가 많아질수록 성능이 떨어진다
epoll
리눅스에서만 지원
select의 단점을 극복하기 위해 Kernel level의 멀티플렉싱을 지원한다.
File Descriptor의 상태를 kernel에서 관리하여 상태가 바뀐 것을 직접 통지한다.
- 변화가 감지된 file descriptor의 개수가 아닌 목록 자체를 반환받기 때문에 대상 파일을 select, poll처럼 loop를 돌며 찾을 필요가 없어 효율적이다.
epoll_wait함수를 호출하면 관찰 대상의 정보를 매번 전달할 필요가 없다.
두 가지 방식
Level-Triggered: 입력 Buffer에 데이터가 남아있는 동안 계속 이벤트를 발생시킨다. 데이터가 존재만 한다면 계속 알려준다.
Edge-Triggered: 입력 Buffer에 데이터가 들어오는 순간에만 이벤트를 발생시킨다.
IOCP (I/O Completion Port)
Windows 환경에서의 Non-Block Socket을 대량으로, 효율적으로 처리해 주는 API이다.
특징
고성능 서버 구축에 적합하다.
다수의 작업자 스레드(worker thread)를 효율적으로 관리해 준다.
입출력 작업 완료 시 알림을 통해 처리한다.
kqueue
FreeBSD와 macOS 등 UNIX 계열 운영체제에서 사용하는 이벤트 통지 메커니즘이다.
특징
소켓, 파일, 타이머 등 다양한 이벤트 모니터링이 가능하다.
비동기 I/O 이벤트를 효율적으로 관리한다.
epoll vs IOCP vs kqueue
항목 | epoll | IOCP | kqueue |
지원 OS | Linux | Windows | FreeBSD, MacOS 등 |
용도 | 대규모 네트워크 I/O 처리 | 비동기 I/O 처리 | 이벤트 기반 I/O 처리 |
작동 방식 | 이벤트 기반 비동기 I/O 처리 | 완료된 작업에 대한 알림 큐 사용 | 이벤트 등록 후 알림 수신 |
이벤트 관리 | epoll_wait() | GetQueuedCompletionStatus() | kevent() |
핸들링 단위 | File Descriptor | I/O Handle | File Descriptor, socket 등 |
성능 | 대규모 클라이언트 연결에 최적화 | CPU 코어 기반 최적화 | 다양한 이벤트 관리 가능, 효율적 |
특징 | Edge-triggered, Level-triggered 지원 간단한 API 제공 스케일러블 | 입출력 작업 완료 후 작업 큐에서 알림 고성능 서버에 적합 스레드 풀 기반 | 파일 I/O, 타이머 등 다양한 이벤트 지원 멀티플렉싱 가능 유연한 구조 |
장점 | 높은 확장성 많은 연결 처리 가능 | 스레드 풀 관리로 CPU 활용 최적화 대규모 작업 처리 적합 | 여러 이벤트 소스 통합 관리 유연하고 강력한 기능 |