소켓은 기본적으로 봉쇄/동기로 만들어진다. 이 모델은 데이터 입출력 부분에서 봉쇄(blocked)된다는 문제점을 가진다.
이 모델로는 하나의 쓰레드에서 두 개 이상의 소켓을 다루기가 힘들다. 이 모델을 바꾸지 않고 두개 이상의 소켓을 처리하려면 "멀티 쓰레드" 기술을 함께 사용하는 수 밖에 없다.
윈도 운영체제(:12)는 멀티 태스킹을 지원한다. 이는 입출력:::모델(:12)과는 상관없이, 커널은 여러 소켓으로 부터의 입력을 처리한다는 의미다. 이기능을 봉쇄/동기 입출력 모델의 한계로 쓰지 못하는 것일 뿐이다.
이 문제는 입출력 모델을 "비동기 / 봉쇄" 혹은 "비동기 / 비봉쇄"을 쓰는 것으로 해결할 수 있다. 비동기 봉쇄 모델을 사용하는 기술이 select(:2)함수를 이용한 입출력:::다중화(:12)이다. Overlapped I/O 모델은 "비동기 / 비봉쇄 모델"의 응용 모델이다. Overlapped에는 non-blocking이 포함되어 있다. 즉 Overlapped 는 non-block + (비동기적) 완료 통보 라고 보면 된다.
Overlapped I/O는 다음과 같이 묘사할 수 있다. (정확한 묘산는 아니다.)
앞서 언급했듯이 커널은 여러 개의 소켓의 데이터를 동시에 다룰 수 있다. 그러다 보면 시간을 축으로 데이터가 중첩이 되는 구간이 생길 수도 있을 것이다. 이제 데이터의 입력이 완료되면, 해당 소켓에서 이벤트를 통지한다. 이벤트를 받은 프로세스는 이벤트 객체를 이용해서 데이터를 처리하거나 혹은 Completion Routine를 이용해서 데이터를 처리할 수 있다. 중첩해서 들어올 수 있는 데이터의 처리를 커널에 맡기게 되므로 유저 모드에서 블럭되는 일 없이 여러 개의 소켓을 처리할 수 있다.
Overlapped I/O의 중첩을 허용하는 이런 특징 때문에, "중첩 입출력 모델"이라고 부르기도 한다. 생소한 기술 같지만, 원리에 있어서는 리눅스(:12)의 입출력:::다중화(:12)나 리얼:::타임:::시그널(:12)과 큰 차이가 없음을 알 수 있다. 다음은 비 동기 / 비 봉쇄 입출력모델을 따르는 프로그램의 흐름을 보여준다.
읽기 함수를 호출하면, 커널은 데이터를 읽기 위한 초기화 작업을 하고 데이터를 기다린다. 읽기 함수는 바로 반환한다.
이제 프로그램은 다른 작업을 진행한다.
데이터가 입력되면, 이벤트가 발생한다.
프로그램은 콜백함수 혹은 이벤트 객체를 만들어서 데이터를 읽어서 처리한다.
하지만 리눅스의 입출력 다중화나 리얼 타임 시그널등 비동기 입출력 기술과 차이점이 있다. 이들 리눅스 모델은 데이터 입력이 완료된 시점에서 데이터를 읽는 방식이 아니다. 소켓으로 데이터가 입력되는 시점에서 커널 데이터를 유저 데이터로 복사한다. 이말은 커널로 부터 데이터의 복사가 끝날 때까지 몇 번이나 입출력 함수를 호출 할 수도 있음을 의미한다. 여러 번 유저 모드와 커널 모드간의 변환이 있으므로 성능이 감소할 수 밖에 없다.
윈도의 Overlapped I/O모델은 데이터 입출력이 완료된 시점에서 이벤트를 발생한다. 이로 인해 얻을 수 있는 이점은 다음과 같다.
모드 변환이 발생하지 않는다. (여러 번의 데이터 복사가 발생하지 않는다.)
데이터 입출력이 진행되는 동안에도 다른 일을 할 수 있다.
Overlapped 소켓 프로그램
socket 함수로 만든 소켓은 기본적으로 중첩 특성을 가진다. 그러나 다른 BSD:::소켓(:12)입출력 함수들로는 중첩 소켓을 다루지 못한다. 그러므로 BSD:::소켓(:12) 입출력 함수대신 WSASend(:4100), WSARecv(:4100), WSASendTo(:4100), WSARecvFrom(:4100) 등의 함수를 이용해야 한다. WSASocket(:4100) 함수로 소켓을 만들 경우에는 WSA_FLAG_OVERLAPPED를 지정해줘야 한다.
중첩 소켓을 사용은 WSAsend(:4100) 함수의 lpOverlapped 매개 변수와 lpCompletionRoutine 매개 변수로 이루어진다.
lpOverlapped : WSAOVERLAPPED(:4300)구조체의 포인터를 넘긴다. 만약 NULL 이라면 중첩 소켓 특성을 사용하지 않는다.
lpCompletionRoutine : 입력이 완료 되었을 때 호출할 완료 루틴을 설정한다. 만약 NULL 이라면, 이벤트 객체 기반으로 처리한다.
이벤트 객체 기반 처리
lpCompletionRoutine이 NULL일 경우, WSAOVERLAPPED 구조체의 이벤트 객체 핸들러로 입력을 처리한다. 이때 accept(:4100)함수를 어떻게 처리해야 할 것이낙 하는 문제가 있다. accept 함수는 중첩 소켓 특성을 이용하지 못한다. 또한 WSAAccept 함수도 중첩 소켓 특성을 이용하지 못한다. accept는 단 1 바이트만 들어와도 요청이 완료되므로, 굳이 중첩 특성을 이용할 필요가 없기 때문이다. acceptEx 함수가 있기는 하지만, 그다지 쓰고 싶지 않다.
다음 두 가지 방법 중 하나를 선택하면 될 것 같다.
멀티 쓰레드(:12) 프로그래밍
accept는 메인 쓰레드에서 처리하고, 입출력은 워커 쓰레드에서 처리하도록 한다. 두 개의 쓰레드가 필요하다.
Contents
Overlapped I/O 모델
Overlapped 소켓 프로그램
이벤트 객체 기반 처리
콜백 함수 호출
Recent Posts
Archive Posts
Tags