• IOCP - 1 (I/O CompletionPort)

    2014. 12. 14.

    by. xxx123

     

     

    1. IOCP를 구성하는 5가지 자료구조

     

    ① Device List

    hDevice는 네트워크에선  Socket이며, CompletionKey는 유일한 키값이다.

    IOCP에 두 인자를 등록 시 DeviceList라고하는 자료구조에 두 값이 묶여서 저장된다.

    이렇게 저장된 값들은 GQCS를 통해 해당 소켓에 데이터가 완료되면 Key와 함께 값을 얻을 수 있게된다.

     

     

    ② I/O Completion Queue

    IOCP 큐라고도 불리며 I/O가 완료되면 그 관련 정보를 저장하는 자료구조이다.

    착각하지 말아야 하는것은 큐에 패킷이 들어가는것이 아니라 그에 대한 정보(이벤트)가 들어간다는 것이다.

    이 큐의 특징은 FIFO라서 앞에서 부터 순차적으로 자료를 빼서 처리한다.

     

     

    ③ Waiting Thread Queue

    스레드 들이 대기하는 큐이며 LIFO로 동작한다. 즉, 가장 마지막에 사용된 녀석이 다음번에 다시 사용될 가능성이 높다.

    실제로 스레드들이 들어가 있는것이 아니라 해당 스레드 ID가 들어가 있다.

    IOCP에서 스레드풀의 개념을 이 큐에서 담당하고 있다고 볼 수 있다.

     

     

    ④ Released Thread List

    스레드 풀. 바로 Waiting Thread Queue에서 꺼내온 쓰레드 정보라고 할 수 있다.

    밑에서 설명하겠지만 이 큐에 들어 갈 수 있는 최대 스레드 갯수는 IOCP 설정 당시 셋팅을 한다.

     

     

     

    ⑤ Paused Thread List

    정지된 스레드 리스트, 블로킹 함수 등에 의해 해당 Release 스레드가 Suspend 상태가 되면 이 리스트로 들어가게 된다.

    어떤 이벤트에 의해 스레드가 Suspend 해제 상태가 되면 다시 Released Thread List로 옮겨간다.

     

     

     

     

    전체적인 그림을 보자면 다음과 같다.

    여기서 봐야할 점은 완료 큐를 통해 얻을 수 있는 정보는 Device(Socket)이나 Buffer 정보가 아닌

    CompletionKey값과 Overlapped 정보라는 것이다.

    이 두 정보를 통해 우리가 필요로 하는 정보를 얻어와야 한다.

     

     

     

     

     

     

     

    2. IOCP에 대한 의문점 및 고찰

     

    ① 최초 생성에서 CreateIoCompletionPort의 인자값인 NumberOfConcurrentThreads

    해당 함수의 인자 값은 생성할 스레드의 갯수가 아니다.

    위에서 설명한 Released Thread List에 들어 갈 수 있는 최대 스레드 갯수이다.

    즉, 동시에 작업을 수행할 스레드의 갯수라고 할 수 있다.

    스레드 당 CPU 하나가 가장 이상적인 퍼포먼스를 내는 스레드 수 이기 때문에 CPU 갯수에 맞춰 이 값을 셋팅한다.

    0 값을 넣으면 자동으로 해당 PC의 CPU 갯수로 세팅된다.

     

     

    ② 스레드 풀이 생성되는 시점

    윈도우가 스레드를 가득담은 풀을 제공해주는 것은 아니다.

    우리가 스레드를 충분히 만들어 두고 각 스레드에서 GQCS()를 호출하여 Blocking됨과 동시에 Waiting Thread Queue로 ID가 들어가게 되면 이것이 풀처럼 동작하는 것이다.

     

     

    ③ LIFO에서 오는 장점

    이 부분은  FIFO일 때와 비교해보면 좋다.

    LIFO 구조로 스레드를 가져다 쓴다고 하면 거의 항상 쓰던 스레드를 사용하게 될것이다.

    이렇게 빈번하게 사용되는 스레드의 정보는 하드로 Swap되지 않을 것이고 캐시의 정보 또한 마찬가지 일것이다.

    하지만 FIFO일 경우 모든 스레드가 동일하게 한번씩을 사용될 것인데. 이럴 경우 호출한지 좀 지난 스레드는

    정보가 하드로 스왑될 것이고 다시 사용할 때 다시 하드에서 가져오는 작업을 해야 하는것이다.

     

     

    ④ 완료 통보에 대한 순서 문제

    동일 소켓에 대해 I/O의 완료처리는 순서가 다를 수 있다. '동일' 이란 부분에 주의하자.

    한 소켓에 100byte Recv와 200byte Recv를 중첩해서 걸어 놨다면 200byte의 완료 처리가 먼저 일어 날 수 있다는 뜻이다.

     

    위에서 설명했듯 IOCP의 완료 통보는 확실히 순서적이지 않을 수 있다.

    하지만 정확히 이해가지 않는 부분은 Overlapped I/O의 I/O 처리에 대한 순서 보장이다.

    Noetwork Programming For Microsoft Windows라는 책을 보면 Overlapped I/O의 처리 동작은 순차적으로 나와있다.

    하지만 이기탁씨의 문서나 Via를 보면 Overlapped I/O의 처리 동작은 비 순서적이라고 한다.

     

    어찌됐듯 동일 소켓에 대해 비순서적인 처리가 일어날 여지가 있다면 해결 방법은 간단하다.

    바로 한 소켓에 한번의 Recv만을 거는 것이다. 그렇게되면 순서가 뒤바뀌거나 할 일은 절대 없을 것이다.

     

     

    ⑤ WorkerThread의 갯수

    WorkerThread로 사용할 스레드의 갯수를 결정할 때 MSDN은  CPU * 2 + 1이라는 알 수 없는 숫자를 추천한다.

    WorkerThread의 갯수를 CPU 갯수보다 크게 잡으라고 하는 의미는 바로

    스레드가 Suspend 될 경우가 있기 때문이다.

     

    스레드를 CPU 수에 딱 맞춰 생성해 놓았는데 해당 스레드가 작업을 처리하던 도중 Blocking 함수를 만나 Paused Thread List로 들어가게 될 경우. 다른 작업이 들어왔을 때 작업을 처리할 스레드가 없어서 다른 스레드의 작업이 끝날 때까지 기다려야 하는 상황이 올 수 있다.

     

    그렇기 때문에 저런 알 수 없는 숫자를 추천한 것이고, 실제로 가장 적절 스레드 수는 테스트를 통해 알아내야 하는 것이 맞다.

     

     

     

    정리를 하자면,

    IOCP란 "OverlappedI/O + ThreadPool + CompletionQueue"라고 할 수 있다.

     

     

    [참고]

    http://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx

    'Programming > Network' 카테고리의 다른 글

    SO_SNDBUF 0  (0) 2016.12.04
    TCP Header  (0) 2015.06.19
    page-locking & non-paged pool  (0) 2015.06.19
    shutdown()과 closesocket()  (1) 2015.06.15
    Overlapped I/O  (0) 2014.09.18

    댓글