[서버-8] Overlapped I/O 혹은 Asynchronous(비동기) I/O

#서버 프로그래밍

논블록 소켓의 단점을 극복한 Overlapped I/O


이전 포스트에서 다룬 논블록 소켓의 단점은 크게 아래 두 가지였다.

  • 소켓 I/O 함수가 리턴한 코드가 would block인 경우 재시도 호출 낭비가 발생한다.
  • 소켓 I/O 함수를 호출할 때 입력하는 데이터 블록에 대한 복사 연산이 발생한다.

Overlapped I/O 또는 Asynchronous(비동기) I/O

Overlapped I/O 또는 Asynchronous I/O를 이용하면 위 두 가지 단점을 한 번에 해결 가능하다.

우선 논블록 소켓은 다음과 같이 데이터를 다룬다.

  1. 소켓에 I/O 가능인 것이 있을 때까지 기다린다.
  2. 소켓에 대한 논블록 액세스를 한다.
  3. would block이 발생하면 그대로 두고 그렇지 않으면 실행 결과 리턴 값을 처리한다.

Overlapped I/O 에서는 다르게 다룬다.

  1. 소켓에 대해 Overlapped 액세스를 건다.
  2. Overlapped 액세스가 성공했는지 확인한 후 성공했으면 결과를 얻어 와서 나머지를 처리한다.

재시도 호출 낭비를 해결

Overlapped I/O 함수는 즉시 리턴되지만 OS로 해당 I/O 실행이 별도로 동시간대에 진행되는 상태이다.

즉, Overlapped I/O를 사용하면 WSARecv()WSASend()같은 네트워크 함수가 즉시 반환된다. 하지만 이 반환은 "작업이 끝났다"는 의미가 아니라 "작업을 OS에 넘겼다." 는 뜻이다.

작업을 위임받은 Device Driver는 I/O 작업이 끝날 때까지 감시한 후 작업이 끝나면 알려준다.

작업이 끝나는 것을 알기 때문에 논블록 소켓에서 발생했던 재시도 호출 낭비가 해결된다.

근데 작업이 끝났는지 어떻게 알 수 있나. Overlapped I/O 함수를 호출할 때 작업의 "진행 상태"를 보관하는 구조체(OVERLAPPED 구조체)를 같이 넘긴다.

작업이 완료되면 Device DriverOVERLAPPED 구조체를 업데이트 하므로 OVERLAPPED 구조체를 확인하면 작업의 완료 여부를 알 수 있다.

데이터 블록 복사에 따른 오버헤드 해결

또한 Overlapped I/O 전용 송수신 함수를 호출하면 OS는 송신할 데이터가 저장되어 있는 메모리 블록 자체를 송신 버퍼로 사용해 버린다. 수신할 때도 마찬가지이다. 즉, 데이터 복사에 대한 오버헤드 부담이 없다.

Overlapped I/OWindows 전용으로 리눅스 등의 타 운영체제에서 찾아보기는 어렵다.

소켓의 개수가 무진장 많을 때

소켓 개수가 1만 개로 무진장 많을 때 논블록 소켓과 Overlapped I/O는 각각 다음과 같이 작동한다.

논블록 방식

  1. 소켓 1만개에 대해서 select를 호출한다.
  2. 각 소켓에서 루프를 돌며 I/O를 시도한다.

Overlapped I/O 방식

  1. 개수가 1만개 이하인 각 OVERLAPPED status 객체에 대해서 GetOverlappedStatus를 실행하여 I/O 완료 상태인지 확인한다.
  2. I/O 완료 상태면 나머지 처리를 수행한다.

두 방식 모두 소켓에 루프를 돌기 때문에 성능 문제와 직결되기 쉽다. 소켓 개수가 많을 때 루프를 돌지 않고 한 번에 해결하는 방식으로 EPOLLIOCP가 있다.