본문 바로가기
서버

Overlapped IO와 IOCP

by PainDiver 2022. 11. 27.

 

 

Overlapped IO 모델

IOCP는 Overlapped  IO모델을 기반으로 확장한 것으로 뗄레야 뗄 수 없는 사이

 

Overlapped IO모델은 어플리케이션은 입출력 함수를 호출하면 그 사이 무관하게 다른 작업을 진행할 수 있다.

그렇다 비동기 논블로킹이다.

이게 어떻게 가능하냐면, 입출력 작업이 끝나면 운영체제는 작업완료를 어플리케이션에 알려준다.

그럼 어플리케이션은 다른 작업을 중단하고 입출력 결과를 처리하면 되는 것이다.

 

IOCP에서 보면, Completion Port를 만들어 두고 소켓을 그곳에 등록하면, 소켓이 입출력작업이 완료된 것을 Worker 쓰레드를 이용해서 GetQueuedCompletionStatus로 완료된 소켓이나 어떤 이벤트인지(이건 Overlapped Event로 구현) 파악하고 그에 해당하는 로직을 실행시키지 않았더나?

 

IOCP 모델

Overlapped IO에서는 운영체제가 작업완료를 어플리케이션에게 알려준다 했는데, 이걸 Completion Port라는 커널이 만든 큐객체를 이용해서 입출력 작업완료 결과를 담아두는 것이다. 이게 IOCP이다.

 

이렇게 하면 장점이 무엇이냐?, 적은 수의 쓰레드로도 많은 입출력처리를 쉽게 할 수있다. 입출력 완료 통지를 받아도 큐에 쌓이기 때문에 바로 완료로직을 실행안시켜도 되고,  Worker 쓰레드를 만들어서 Completion Port를 전문적으로 처리해주는 녀석들이 시간 날 때 실행시킬 수 있다는 말이다.

 

즉 한마디로 정리하자면, Overlapped IO는 IO작업을 운영체제에게 통지 받을 수 있어서 비동기 논블로킹으로, 실행할 수 있다는 게 맹점이고, IOCP는 Overalapped IO의 작업완료 통지를 큐에 담아서 필요할 때 꺼내어 쓸 수 있다는 것이 맹점이다.

 

그럼 이걸 어떻게 같이 쓰느냐?

 

일단 WSASocket은 필수다. 확장기능을 사용해야 Overlapped IO를 사용할 수 있기 때문이다.

 

iocp를 만들고 iocp 핸들에 등록시켜줘야한다. 근데 여기서 쓰이는 함수가 똑같다. CreateIoCompletionPort 함수인데, 매개변수만 다를 뿐이다.

 

자 그럼 이제 Send나 Recv같은건 어떻게 하느냐? WSARecv나, WSASend 처럼 확장 기능을 가진 함수들은 Overlapped 구조체를 파라미터로 받는다. Overlapped IO쓸꺼면 이 함수들을 써라.

 

확장함수들은 Overlapped 구조체를 파라미터로 받아서 운영체제에 의해서 완료통지가 되면 이 Overlapped구조체를 꺼내서 다시 쓸 수 있다.

 

근데 이게 IOCP를 사용할 때 쓰기 좋다!

 

왜냐? IOCP를 사용하면 Completion Port에 완료된 입출력이 쌓인다 했다. 그 때 아까 같이 overlapped 구조체로 등록한 그 녀석을 고대로 꺼내어 쓸 수 있다. 이 말은 즉, 아까 넣은 Overlapped 구조체 안에 어떤 소켓이, 어떤 작업을 실행시켰는지 사용자 정의로 별 정보를 넣어서 완료통지가 되었을 때 알 수 있다는 의미이다.

 

그래서 이걸 응용하면, Overlapped 구조체를 상속받아서 Event 클래스를 만들게 되면, Event클래스는 Overlapped 형태로 형변환 될 수 있으므로, 이를 고대로 확장함수에 넣어버리면, 나중에 GetQueuedCompletionStatus에서 그냥 Overlapped 부분을 빼와서 형변환을 한 다음 타입체크 후, 해당 작업 로직을 실행시켜주면 된다는 것 이다.

 

class IOCPEvent :public OVERLAPPED
{
public:
IOCPEvent(EventType type);

void Init();
EventType GetType() { return _eventType; }
std::shared_ptr<IOCPObject> GetOwner() {return _owner; }
void SetOwner(std::shared_ptr<IOCPObject> owner) { _owner = owner; }

protected:
EventType _eventType;
std::shared_ptr<IOCPObject> _owner;
};

 

요런 느낌으로 OVERLAPPED 구조체를 상속 받는 IOCPEvent를 이용해서, 확장시키면, RecvEvent, SendEvent 등 여러 타입을 만들어서 GetQueueCompletionStatus에서 해석해서 그에 따라 로직을 실행 시 킬 수 있다.

 

'서버' 카테고리의 다른 글

셀렉트 모델  (0) 2022.11.27
Zero-Copy 이게 뭐더냐?  (0) 2022.11.25
블로킹 vs 논블로킹? 동기 vs 비동기?  (1) 2022.11.25