IOCP

library/WIN32API 2010. 7. 8. 02:25

* 이기탁님이 작성한 문서를 요약함

1. Overlapped IO

 1.1. 넌블러킹, 비동기적으로 IO처리
 1.2. 쓰레드가 Device Driver에게 IO요청 후 신경쓰지 않음
 1.3. Device Driver가 IO감시하면서 완료되면 알려 줌
  1) 이벤트 객체 멤버 이용
  2) 콜백함수
  3) 직접 조회
  4) IOCP
 1.4. IO blocking없음, FIFO로 IO처리하지 않음
 1.5. 소켓생성
      SOCKET sock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
 1.6. 송신
  int WSASend(SOCKET s,
        LPWSABUF lpBuffers,
              DWORD dwBufferCount,
              LPDWORD lpNumberOfBytesSent,
              DWORD dwFlags,
              LPWSAOVERLAPPED lpOverlapped,
              LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
  1) IO가 끝나기 전에 lpOverlapped가 메모리에서 사라지면 안됨.
  2) lpCompletionRoutine: 1.3.2)의 콜백함수
  3) 리턴값이 SOCKET_ERROR이면서 WSAGetLastError()==ERROR_IO_PENDING인 경우는
     IO진행중이라는 의미

2. IOCP

 2.1. 커널 오브젝트중 하나
 2.2. 재사용 가능한  쓰레드풀 유지
 2.3. CPU에 dispatch되는 쓰레드 조절
 2.4. 생성
  HANDLE CreateIoCompletionPort (
  HANDLE FileHandle,              // handle to file
  HANDLE ExistingCompletionPort,  // handle to I/O completion port
  ULONG_PTR CompletionKey,        // completion key
  DWORD NumberOfConcurrentThreads // number of threads to execute concurrently);
    두가지 일을 한다.
    1) IOCP커널 객체 생성
        hiocp = CreateCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 쓰레드숫자);
    2) Device와 IOCP객체 연결
        CreateCompletionPort(sock, hIocp, 컴플리션 키, 0 );
 2.5. 쓰레드풀 생성
 2.6. 동작에 필요한 자료구조
  2.6.1. Device List
   1) hDevice - dwCompletionKey
   2) hDevice와 관련된 IO가 완료되었을 때 dwCompletionKey를 던짐
   3) 생성: CreateIoCompletionPort가 호출될 때
   4) 제거: hDevice가 close될 때

  2.6.2. IO Completion Queue(IOCQ)
   1) dwBytesTransferred - dwCompletionKey - pOverlapped - dwError
   2) IO의 결과를 저장
   3) worker 쓰레드에서 하나하나 빼서 처리
   4) PostQueuedCompletionStatus함수를 이용해서 직접 넣을 수 있음.
      쓰레드간 큐나 쓰레드 종료 시 활용
   5) 생성: - IO 요청이 완료되었을 때 
            - PostQueuedCompletionStatus를 호출하였을 때
      제거: - Waiting Thread Queue로부터 Entry를 하나 제거할 때
              즉 IOCP Worker Thread를 하나 깨우고 이 쓰레드에서 레코드 하나를
              꺼낼 때

  2.6.3. Waiting Thread Queue(WTQ)
   1) dwThreadid
   2) IOCP Worker 쓰레드 풀
   3) GetQueuedCompletionStatus(GQCS)함수가 호출되면 Worker 쓰레드가 이 큐에
      쌓고 대기.
   4) 2.6.2의 IOCQ에 레코드가 들어오면 IOCP가 이 큐에서 쓰레드 하나를 깨우고
      GQCS함수를 리턴 시킴
      (CreateIoCompletionPort호출시 마지막 인자인 Concurrent Thread 개수만큼만)
   5) 실제로는 LIFO구조인데 Queue라는 이름이 붙었음
      LIFO구조를 이용해서 사용되는 쓰레드수를 최소화 함(CPU scheduling이 최소화됨)
   6) 제거: IOCQ가 비어있지 않고 Release Thread List(RTL)에 있는 쓰레드 수가
            Concurrent Thread수를 넘지 않을 때, IOCQ에서 레코드 entry가 하나
            제거되고, WTQ에 있던 dwThreadid가 RTL로 옮겨감.

  2.6.4. Released Thread List(RTL)
   1) 쓰레드 풀에서 꺼내온 쓰레드들
   2) 생성: - IOCP가 WTQ에서 쓰레드를 깨울 때
            - Paused Thread가 깨어날 때
   3) 제거: - 쓰레드가 다시 GetQueuedCompletionStatus 함수를 부를 때
            - 쓰레드가 스스로 Suspend(Sleep, WaitForSinglObject등으로 블록) 될 때

  2.6.5 Paused Thread List(PTL)
   1) 어떤 이유로 Suspend된 쓰레드들의 모임
   2) 생성: Released Thread 즉 돌고 있는 쓰레드가 스스로 멈출 때
      제거: Suspend된 쓰레드가 깨어날 때(dwThreadid는 RTL로 옮겨감)

  2.6.6. Concurrent Thread 수와 PTL, RTL
   1) Concurrent Thread 수: RTL에 들어갈 쓰레드 수
   2) Suspend된 쓰레드가 있다면 PTL에에 옮겨 놓고 WTQ에 대기중인 쓰레드를
      RTL로 가져와 처리.
   3) Suspend된 쓰레드가 깨어나면 RTL에 쓰레드가 Concurrent Thread 수보다
      초과될 수도 있다. 적어질 때까지 IOCP는 WTQ에서 쓰레드를 가져오지
      않는다. (효율적인 Thread Context Switching)

블로그 이미지

란마12

,