IOCP(I/O Completion Port)란 무엇인가?
소켓이나 파일의 입출력을 최소한의 스레드를 사용해서 처리하는 기법이다. 이를 위한 스레드 풀링이나 비동기 처리등을 운영체제에서 관리해주기 때문에 사용자는 IOCP를 이용해서 쉽고 빠르게 고성능의 입출력 처리가 가능해진다.
추가적으로 설명하자면, 중첩 입출력(Overlapped I/O)이 완료되면 사용자에게 이를 통지해 주는 커널 오브젝트이다.
IOCP는 비동기(Asynchronous) + 스레드 풀링(Thread Pooling) + 논 블로킹(Non-Blocking) + 중첩 입출력(Overlapped I/O)과 같은 개념들을 이용해서 작동한다.
IOCP의 장점
- 스레드 풀(Thread Pool)을 쉽게 사용할 수 있다. (운영체제가 직접 스레드 풀링 관리)
- 스레드를 효율적으로 사용하므로 CPU 자원소모가 줄어든다.
- Context Switching 비용이 줄어든다.
- winsock2 API 중에서 확장성과 성능이 뛰어나다.
IOCP의 단점
- 프로그램 구현이 복잡해진다.
- Window에서만 사용이 가능하다.
- 하나의 I/O operation마다 버퍼 영역에 대한 page-loock/unlock이 필요하다.
- 이를 회피하기 위한 zero-byte, zero-byte recv
- 하나의 I/O operation마다 시스템 콜이 발생한다.
IOCP를 이용한 입출력 과정
- 완료 포트(Completion Port) 생성
I/O 작업의 완료 알림을 받을 수 있는 커널 오브젝트이다. - I/O 객체와 Completion Port 객체 연결
I/O 객체(일반적으로 네트워크 소켓이나 파일)를 생성하고 I/O 객체를 Completion Port와 연결한다. 이렇게 하면 I/O 작업의 완료 알림을 받을 수 있다. - 작업 스레드 생성
일반적으로 스레드 풀을 사용해 작업자 스레드를 생성한다. 작업자 스레드는 Completion Port에서 알림을 받아 작업을 처리한다. - 작업자 스레드 대기
작업자 스레드는 GetQueuedCompletionStatus 함수를 사용해 CompletionPort의 알림을 기다린다. - I/O 작업 완료 알림 처리
스레드 풀링(Thread Pooling)
스레드 풀링은 한번 생성한 스레드를 재활용해서 시스템의 부하를 덜어주기 위한 기법이다. 이는 스레드의 생성과 소멸에 소모되는 리소스가 상당하기 때문에 등장한 것이다. 기본적으로 개념은 다음과 같다. 일단 여러 개의 스레드를 생성한다. 그리고 실행해야 할 일이 등록될 때마다 미리 생성해 놓은 스레드 중 하나를 할당한다. 그리고 일이 끝나면 스레드는 소멸시키지 않고 다음 일을 위해서 보관한다.
스레드 풀은 처리해야 하는 할 일이 등록되기 전에 생성되는데, 풀이 생성됨과 동시에 스레드들도 생성되어 풀에서 대기하게 된다. 지능적인 풀은 처리해야 할 일의 증가 및 감소에 따라 풀 안의 스레드 개수를 늘리기도 하고, 줄이기도 한다.
스레드 풀이 생성된 상태에서 처리해야 할 일이 하나 등록되었다고 가정해 보자. 그렇다면 스레드 풀에 존재하는 스레드 하나를 임의로 할당해서 일을 처리한다. 만약에 풀에 존재하는 스레드 수보다 처리해야 할 일의 수가 많다면, 일이 순서대로 처리되도록 디자인할 수도 있고, 빠른 일 처리를 위해 추가적인 스레드가 생성되도록 풀을 디자인할 수도 있다.
블로킹(Blocking) vs 논 블로킹(Non-Blocking)
블로킹은 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면 다른 작업이 끝날 때까지 기다렸다가 작업을 진행함
논 블로킹은 다른 주체의 작업에 상관없이 자신의 작업을 진행함
중첩 입출력(Overlapped I/O)
기본적으로 I/O 작업은 동기로 이루어진다. 예를 들어 ReadFile(파일을 읽어 들이는 메서드) 함수를 호출 시 읽기 작업이 끝나기 전까지 함수는 값을 반환하지 않고 처리를 기다리게 된다. 즉, 동기적으로 실행된다.
이렇게 해당 스레드에서 작업이 끝날 때까지 다른 작업을 수행하지 못하고 대기해야 하는 상태를 블로킹(Blocking) 모드라고 한다. 이와 반대로 논 블로킹(Non-Blocking) 모드는 I/O 작업을 호출하고 바로 반환하기 때문에 해당 스레드는 바로 다른 작업을 수행할 수 있다.
Overlapped I/O의 동작방식
- I/O 작업을 요청한다.
- WSASend(..., lpOverlapped) 함수를 호출 (이 함수는 데이터를 전송하는 역할을 담당하며 Overlapped IO 구조체를 파라미터로 전달한다.)하고 바로 리턴된다.
- 비동기적으로, I/O 작업을 요청했다는 정보를 Device Driver에게 전송함. I/O 작업을 Device Driver에게 전송한 스레드는 더 이상 I/O 작업을 신경 쓰지 않는다.
- Device Driver는 I/O 작업이 끝날 때까지 감시한다. 그리고 작업이 완료되면 끝났음을 통보해 준다.
4번까지의 과정을 요약하면 I/O 처리 요청을 스레드가 Device Driver에게 위임하고 I/O 작업이 종료될 때까지 해당 스레드는 다른 작업을 수행할 수 있도록 하는 것이 핵심이다.
어떻게 I/O의 완료 여부를 확인할 것인가?
- Overlapped I/O는 WSASend() 함수의 파라미터로 전달한 overlapped 구조체에 이벤트 커널 오브젝트를 등록하고, 등록된 이벤트 객체를 signaled 상태로 변경해 I/O의 완료 여부를 알린다.
- 스레드는 WSAGetOverlappedResult 함수를 호출하면서 I/O의 완료 및 결과를 확인한다.
함께 읽으면 좋은 글
참고 사이트
'프로그래밍 > 네트워크' 카테고리의 다른 글
공인 IP와 사설 IP에 대해서 (0) | 2024.02.01 |
---|---|
NIC이란 무엇인가? (0) | 2024.01.30 |
AirDrop은 어떻게 작동하는가? (3) | 2023.12.07 |
MAC Address란 무엇인가? (0) | 2023.11.28 |
VPN 터널링 형식 정리 (1) | 2023.11.28 |
댓글