IOCP model

C++ 2010. 10. 19. 20:40
I/O Completion Port (IOCP)
<IOCP 함수간 관계> 

동작 방식 및 특징
요청이 들어오면 처리비용이 가장 높은 IO 작업을 커널 영역에서 처리해주고 처리 결과를 큐에 담는다.
(빠른이유1 : OS 커널에서 지원하는 IO처리)

이제 유저영역에선 큐에서 요청을 가져와(GetQueuedCompletionStatus 함수) 여러 스레드에서 처리한다.
이렇게 커널 영역에서 유저 영역으로의 데이터 전달시 동일한 버퍼를 사용한다.
(빠른이유2 : 커널영역과 유저영역의 버퍼 공유)

요청을 처리하는 스레드들도 Leader/Follow 패턴을 따르는 Thread pool을 
사용하기 때문에 context switching이 일어나지 않는다.
(빠른이유3 : context switching이 없음)
(context switching이 일어나지 않는다는건 잘못된 말 같다.
일어나지 않는게 아니고 방지하도록 만들 수 있다는 것으로 이해하고 있다.
일단 Thread pool을 통해 thread를 미리 생성하고 재활용 할수 있어 빠를 수 있으며
일반적인 IOCP 샘플을 보면 thread 개수를 CPU 코어 개수 만큼이나 2배로 잡는데
(경험적인 노하우로 얻어진 적정 수치로 알고 있음)
이렇게 CPU 단위의 thread 개수 선택으로 인해 context switching을 방지하는 것으로 보여진다.)

또한 Overlapped IO(비동기적인 IO)를 지원. (속도 향상)
polling이 아닌 notification 방식 사용. (속도 향상)
IO 스케쥴링이 FIFO 방식이 아닌 SCAN방식으로 물리적으로 가까운 곳의 IO처리. (속도 향상)
RAID 구성을 사용할 경우 더 빠른 처리가 가능. (속도 향상)
의 특징을 갖고 있다.

메세지 처리에서 콜백 함수 사용하는 방법과 이벤트 커널 오브젝트를 사용하는 방법이 있다.


관련 함수 설명
소켓 : IOCP 객체 : 스레드
n    : 1         : n(보통 CPU 코어 개수 * 2)

적은 IOCP 객체와 적은 스레드의 사용으로 많은 소켓 처리시 우수한 성능

HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads);
IOCP 객체 생성 및 소켓과 연결.
/*
1st, 2nd : 
case 1 : IOCP 객체 생성 - 1st는 INVALID_HANDLE_VALUE(-1), 2nd는 NULL 일 경우 IOCP 객체 핸들 리턴
case 2 : IOCP 객체와 소켓 연결 : 1st는 소켓핸들, 2nd는 IOCP 객체
3rd : IO완료 통보시 IO완료 대기 함수로 전달될 32bit 값.
4th : 동시에 깨울 수 있는 스레드 개수. 0이면 자동으로 최대 CPU 코어 개수만큼.
(IOCP 객체 생성시에만 유효함.)
*/
EX : IOCP 생성)
g_hCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (g_hCP == NULL) return 0;//error
EX : IOCP 객체와 소켓 연결)
if (CreateIoCompletionPort((HANDLE)commsock, g_hCP, (DWORD)commmsock, 0) != g_hCP)
return 0; //error
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytes, PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);
비동기 IO 완료를 대기.
/*
1st : IOCP 객체 핸들
2nd : 입출력이 완료된 bytes 크기
3rd : CreateIoCompletionPort()를 호출해 소켓과 IOCP 객체 연결시 3rd 인자로 전달한 32비트 값을 반환받기 위한 인자.
4th : 비동기 입출력 함수에 전달한 WSAOVERLAPPED 구조체를 반환받기 위한 인자.
5th : 타임아웃시간(ms). INFINITE지정시 무한 대기. 
*/
BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped);
IOCP 객체에 가상의 완료를 통보.
인자로 전달된 값을 이용해 가상의 완료 패킷을 하나 만들어 IOCP 완료 큐에 보내주는 함수. (한번 호출시 하나의 스레드가 깨어남)
즉, GetQueuedCompletionStatus()를 깨우는 함수.
/*
1st : 완료 패킷을 보낼 IOCP 객체 핸들.
2nd : 입출력 완료된 가상의 바이트. GetQueuedCompletionStatus()의 두번째 인자로 반환됨.
3rd : 어플리케이션이 정의하는 32bit 값. GetQueuedCompletionStatus()의 세번째 인자로 반환.
4th : GetQueuedCompletionStatus()dml 4th 인자로 반환될 WSAOVERLAPPED 구조체 포인터
*/
GetQueuedCompletionStatus()가 깨어날때 입출력 완료로 깨어났는지 PostQueuedCompletionStatus()로 깨어났는지 확인 방법.
PostQueuedCompletionStatus(g_hCP, 1, 0, NULL); //3,4번째 인자로 0, NULL을 전달하기 때문에
..
int nRet = GetQueuedCompletionStatus(g_hCP, &cbTransferred, (LPWORD)&sock, (LPOVERLAPPED*)&pOV, INFINITE); //3,4번째 인자가 0, NULL로 세팅됨.
if (sock == 0 && pOV == NULL)
//PostQueuedCompletionStatus()에 의해 깨어남.


IOCP 기본 구조 Server 샘플
인터넷에 있는 소스

IOCP Server 샘플(버그)

IOCP Server 샘플(정상)