Search

I/O Multiplexing

생성일
2023/05/29 02:00
태그

입출력 다중화(I/O Multiplexing)

하나의 통신 채널을 통해서 둘 이상의 데이터를 전송하는데 사용되는 기술로, 물리적 장치를 최소한으로 사용하여 효율적으로 데이터를 전송하는 기술이다.
소켓 통신에서의 입출력 다중화는 다수의 스레드나 다수의 프로세스를 생성하지 않고, 단일 스레드에서 여러 개의 클라이언트에 서비스를 제공하는 것을 말한다. 일반적으로 프로세스를 생성하고 관리하는데 메모리, context switching의 오버헤드, IPC(프로세스 간 통신) 등 많은 작업과 리소스가 요구되는데, 입출력 다중화를 통해 이를 해결할 수 있다.
소켓이 하나의 파일 디스크립터를 통해 관리되는 점에서 착안되었고, 서버 내에서의 읽기/쓰기뿐 아니라 서버-클라이언트 간 네트워크 통신에도 적용이 된다.
I/O Multiplexing는 경우마다 다를 수 있지만 Synchronize + Non-Blocking이나 Asynchronize + Non-Blocking 방식을 통해 구현할 수 있다.

I/O Multiplexing의 구현

IO Multiplexing은 이벤트 루프(Event Loop)라고 하는 메인 루프에서 작동하며, 이벤트 루프는 여러 개의 입출력 작업을 관찰하고, 준비된 작업에 대한 알림을 받아 처리한다.
이벤트 루프는 입출력 작업을 모니터링하기 위해 대기 상태를 유지하고, 대기 상태에서는 여러 개의 입출력 작업에 대한 알림을 기다리면서 다른 작업을 수행할 수 있다. 모니터링은 프로그램의 상태를 주기적으로 검사해서 일정한 조건을 만족할 때 송수신 데이터를 처리하는 polling 방식을 사용한다.
polling은 select, poll, kqueue과 같은 다중 입출력 모델 시스템 함수를 호출하여 여러 개의 파일 디스크립터를 모니터링하고, 데이터의 송수신을 감지하여 요청된 작업을 처리한다.

Select

클라이언트의 파일 디스크립터를 배열에 넣어둔 후 순차적으로 검색하며 데이터 변화를 감지하고 요청을 처리하는 방식이다.
이렇게 파일 디스크립터를 배열 형태로 담고 있는 구조체를 fd_set이라 하는데, 해당하는 파일의 변경이 감지되면 비트값을 1로 변경하고 fd_set의 비트값들을 검사하여 변경된 파일을 확인한다. select()를 호출하게 되면 비트값이 1인 필드의 개수를 반환하고, fd_set을 다시 훑으면서 어떤 필드가 변경되었는지 다시 찾아야한다. 이러한 구조 특성상 당연히 연결 클라이언트(파일 디스크립터)가 늘어날수록 느려진다.
FD_ZERO를 통해 값을 초기화 한 후, 서버와 클라이언트의 파일 디스크립터를 FD_SET으로 지정하고 FD_ISSET을 통해 변경 여부를 감지한다.

Poll

select와 비슷하게 처리하나, Blocking 상태에서 하나 이상의 파일 디스트립터에서 이벤트가 발생하면 Blocking 해제 후 작업을 수행한다.
struct pollfd { int fd; short events; short revents; };
C
복사
위와 같은 구조체를 통해 파일 디스크립터와 처리할 이벤트를 저장할 수 있고, 이벤트가 발생하면 revents에 발생한 이벤트가 저장되어 확인할 수 있다.
이벤트의 종류는 다음과 같다
POLLIN : 읽을 데이터가 있음
POLLPRI : 긴급 데이터(Out-Of-Band) 있음
POLLOUT : 쓸 데이터가 있음
POLLERR : 오류 발생
POLLHUP : Hang up 상태
POLLNVAL : 유효하지 않은 요청

select vs poll

최대 파일 디스크립터 수가 1024개로 제한된 select와 달리 파일 디스크립터의 제한이 없다. 또한 항상 최대 fd_set만큼 loop를 도는 select와는 달리 최대 파일 디스크립터 개수만큼만 돌도록 구현할 수 있어 적은 수의 클라이언트 연결에 효율적이다.
하지만 select는 이벤트 하나에 3bit만큼만 사용하는데, poll은 64bit 가량의 메로리를 사용하기 때문에 파일 디스크립터 수가 많아지면 성능이 select보다 더 떨어질 수 있다.