본문 바로가기

CS/프밍 잡지식

Overlapped I/O 알아보기 - 2

개요

https://basaeng.tistory.com/93

 

Overlapped I/O 알아보기 - 1

개요오늘은 고성능 서버에서 사용하는 Overlapped IO에 대해서 알아보겠습니다.Overlapped IO는 말그대로 입출력을 중첩하는 방식입니다. 자세히 알아보기 전에 먼저 쉬운 이해를 위해 blocking, non-blockin

basaeng.tistory.com

저번 포스팅에 이어 Overlapped I/O 특히 Completion Port 모델에 대해 알아보겠습니다.

 

Completion Port는 Windows 운영체제에만 존재하는 기능입니다.

https://learn.microsoft.com/ko-kr/windows/win32/fileio/i-o-completion-ports

 

I/O 완료 포트 - Win32 apps

I/O 완성 포트는 다중 프로세서 시스템에서 여러 비동기 I/O 요청을 처리하기 위한 효율적인 스레딩 모델을 제공합니다.

learn.microsoft.com

 

APC큐와 Completion Port의 차이점

Completion Routine 방식에서의 APC 큐와는 다르게 Completion Port는 서로 다른 스레드의 입출력도 통지 받을 수 있다는 특징이 있습니다.

 

또한 APC 큐의 결과를 처리하기 위해서는 Alertable Wait상태에 진입해 처리했지만 Completion Port에서는 GetQueuedCompletionStatus()함수 (이하 GQCS)를 호출해야 합니다.

https://learn.microsoft.com/ko-kr/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus

 

GetQueuedCompletionStatus 함수(ioapiset.h) - Win32 apps

지정된 I/O 완료 포트에서 I/O 완료 패킷을 큐에서 제거하려고 시도합니다.

learn.microsoft.com


Completion Port의 입출력 과정

1. 스레드에서 비동기 입출력을 요청한다.

2. 스레드에서 GQCS를 호출해 대기한다.

3. 장치에서 입출력을 완료하면 OS가 Completion Port에 입출력 결과를 보내준다.

4. OS는 해당 포트에 대기 중인 스레드를 하나 골라서 깨우고, 일어난 스레드는 입출력 결과를 처리한다.


IO Copmletion Port 생성법

https://learn.microsoft.com/ko-kr/windows/win32/fileio/createiocompletionport

 

CreateIoCompletionPort 함수(IoAPI.h) - Win32 apps

I/O(입출력) 완료 포트를 만들고 지정된 파일 핸들에 연결하거나, 아직 파일 핸들에 연결되지 않은 I/O 완료 포트를 만들어 나중에 연결할 수 있습니다.

learn.microsoft.com

HANDLE WINAPI CreateIoCompletionPort(
  _In_     HANDLE    FileHandle,
  _In_opt_ HANDLE    ExistingCompletionPort,
  _In_     ULONG_PTR CompletionKey,
  _In_     DWORD     NumberOfConcurrentThreads
);

 

FileHandle: Completion Port와 연결할 handle을 넣습니다. 만약 INVALID_HANDLE_VALUE를 넣는다면 새로운 Completion Port를 생성하겠다는 의미입니다.

ExistingCompletionPort: 연결할 port의 handle을 넣습니다. 만약 FileHandle이 INVALIE_HANDLE_VALUE였다면 NULL을 넣습니다.

CompletionKey: 입출력이 완료되면 Completion Key에 대한 값을 돌려줍니다. 이를 통해 빠른 검색 등을 수행해볼 수 있습니다.

NumberOfConcurrentThreads: 새로운 포트를 생성할 때만 사용되는 값입니다. 해당 포트가 동시에 관리할 수 있는 최대의 스레드 수를 지정합니다.


GetQueuedCompletionStatus로 입출력 통지 받기

BOOL GetQueuedCompletionStatus(
  [in]  HANDLE       CompletionPort,
        LPDWORD      lpNumberOfBytesTransferred,
  [out] PULONG_PTR   lpCompletionKey,
  [out] LPOVERLAPPED *lpOverlapped,
  [in]  DWORD        dwMilliseconds
);

CompletionPort: 생성한 포트를 지정합니다.

lpNumberOfBytesTransferred: recv, send한 바이트를 받는 인자입니다.

lpCompletionKey: create시에 사용했던 completion key를 받을 out 인자입니다.

lpOverlapped: 입출력 시 사용되었던 overlapped 구조체 포인터를 반환하는 인자입니다.

dwMilliseconds: GQCS상태에서 대기할 시간을 결정합니다.

 

만약 제한 시간 내에 반환되지 못한다면, GQCS는 FALSE를 반환하고 lpOverlapped = nullptr이 됩니다.


지금까지는 이론적인 간단한 내용이었고 다음 포스팅에서는 실제로 사용하는 것을 다뤄보겠습니다.