[서버-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
를 이용하면 위 두 가지 단점을 한 번에 해결 가능하다.
우선 논블록 소켓은 다음과 같이 데이터를 다룬다.
- 소켓에 I/O 가능인 것이 있을 때까지 기다린다.
- 소켓에 대한 논블록 액세스를 한다.
- would block이 발생하면 그대로 두고 그렇지 않으면 실행 결과 리턴 값을 처리한다.
Overlapped I/O
에서는 다르게 다룬다.
- 소켓에 대해 Overlapped 액세스를 건다.
- Overlapped 액세스가 성공했는지 확인한 후 성공했으면 결과를 얻어 와서 나머지를 처리한다.
재시도 호출 낭비를 해결
Overlapped I/O 함수는 즉시 리턴되지만 OS로 해당 I/O 실행이 별도로 동시간대에 진행되는 상태이다.
즉, Overlapped I/O를 사용하면 WSARecv()
와 WSASend()
같은 네트워크 함수가 즉시 반환된다. 하지만 이 반환은 "작업이 끝났다"는 의미가 아니라 "작업을 OS에 넘겼다." 는 뜻이다.
작업을 위임받은 Device Driver
는 I/O 작업이 끝날 때까지 감시한 후 작업이 끝나면 알려준다.
작업이 끝나는 것을 알기 때문에 논블록 소켓에서 발생했던 재시도 호출 낭비가 해결된다.
근데 작업이 끝났는지 어떻게 알 수 있나. Overlapped I/O 함수를 호출할 때 작업의 "진행 상태"를 보관하는 구조체(OVERLAPPED
구조체)를 같이 넘긴다.
작업이 완료되면 Device Driver가 OVERLAPPED
구조체를 업데이트 하므로 OVERLAPPED 구조체를 확인하면 작업의 완료 여부를 알 수 있다.
데이터 블록 복사에 따른 오버헤드 해결
또한 Overlapped I/O 전용 송수신 함수를 호출하면 OS는 송신할 데이터가 저장되어 있는 메모리 블록 자체를 송신 버퍼로 사용해 버린다. 수신할 때도 마찬가지이다. 즉, 데이터 복사에 대한 오버헤드 부담이 없다.
Overlapped I/O
는 Windows
전용으로 리눅스 등의 타 운영체제에서 찾아보기는 어렵다.
소켓의 개수가 무진장 많을 때
소켓 개수가 1만 개로 무진장 많을 때 논블록 소켓과 Overlapped I/O는 각각 다음과 같이 작동한다.
논블록 방식
- 소켓 1만개에 대해서
select
를 호출한다. - 각 소켓에서 루프를 돌며 I/O를 시도한다.
Overlapped I/O 방식
- 개수가 1만개 이하인 각
OVERLAPPED status
객체에 대해서GetOverlappedStatus
를 실행하여 I/O 완료 상태인지 확인한다. - I/O 완료 상태면 나머지 처리를 수행한다.
두 방식 모두 소켓에 루프를 돌기 때문에 성능 문제와 직결되기 쉽다. 소켓 개수가 많을 때 루프를 돌지 않고 한 번에 해결하는 방식으로 EPOLL
과 IOCP
가 있다.