§IOCP : Input Output Completion Port
§Win32 에서 제공하는 커널 오브젝트 중에 하나.



IOCP는 비동기 I/O작업을 지원하면서 적은 수의 스레드로 최대한 요청을 처리하기 위한 방법입니다.

비동기적으로 I/O가 완료되면 하나의 정보단위가 만들어지고 이것은 IOCP큐에 들어가게 됩니다

이 때 작업 스레드는 IOCP의 통지를 받고 IOCP큐에서 정보 단위 하나를 가져와 작업을 수행하게 됩니다.

작업 스레드가 많으면 좋겠지만,

스레드가 많게되면 스레드를 차지하려고 context switching이 많이 일어나므로 적은 수의 스레드를 사용합니다.








CP오브젝트를 3개 생성

소켓과 CP연결 된 상태를 1, 2, 3이 입출력이 완료된 상태 일 때 CP Q 들어간다.

운영체제가 일이 끝났는지 확인을 하는데 끝났으면 Worker Thread로 만들어서 다른 일을 하게 한다.




동작 방식 및 특징
요청이 들어오면 처리비용이 가장 높은 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()에 의해 깨어남.


'Programing > 소켓 프로그래밍' 카테고리의 다른 글

Overlapped I/O 모델  (0) 2016.11.30
WSAEventSelect 모델  (0) 2016.11.30

+ Recent posts