입출력 다중화(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보다 더 떨어질 수 있다.