-
서버는 클라이언트에 의해 입력과 출력 처리를 많이 진행하게 된다
-
이에, 입력 출력 처리를 효율적으로 사용할 수 있도록 돕는 것이 IOCP 이다.
-
아래는 IOCP를 이해하기 위한 기초 개념들을 정리해 두었다.
입력 및 출력 I/O 개념
-
컴퓨터와 같은 정보 처리 시스템과 다른 컴퓨터 시스템, 주변 장치 또는 인간 작업자와 같은 외부 세계 간의 통신을 지칭함
-
I/O란 서로 다른 두 물체 간 정보를 전송하는 것
-
파일 버퍼링 : 버퍼링되지 않은 파일 입출력(I/O)의 애플리케이션 제어에 대한 고려 사항
( 버퍼링되지 않은 파일 입출력(I/O) : 데이터를 읽거나 쓸 때 별도의 메모리 버퍼를 사용하지 않고 직접적으로 파일에 접근하는 방식 ) -
파일 캐싱 : Windows는 디스크에서 읽은 파일 데이터와 디스크에 쓴 파일 데이터를 캐시함 ( 캐시 : 컴퓨터 내부의 임시 저장 공간으로, 데이터나 연산 결과를 저장하여 반복적인 데이터 접근 시 성능을 향상시키는 기술 )
-
동기 및 비동기 I/O : 입력/출력(I/O) 동기화에는 동기 I/O와 비동기 I/O의 두 가지 유형이 있음
( 비동기 I/O는 오버랩(Overlapped) I/O라고도 함 ) -
보류 중인 I/O 작업 취소 : 느리거나 차단된 I/O 요청을 사용자가 취소할 수 있도록 하면 애플리케이션의 유용성과 견고성이 향상될 수 있음
-
경고 가능한 I/O : 애플리케이션 스레드가 경고 가능한 상태에 있을 때만 비동기 I/O 요청을 처리하는 방법
-
I/O 완료 포트(IOCP) : 멀티프로세서 시스템에서 여러 개의 비동기 I/O 요청을 처리하기 위한 효율적인 스레딩 모델
( 멀티프로세서는 한 컴퓨터 시스템 내에서 두 개 이상의 CPU가 장착되어 있는 구조를 말함 )
동기 및 비동기 I/O
-
입력/출력(I/O) 동기화에는 동기 I/O와 비동기 I/O의 두 가지 유형이 있음
(비동기 I/O는 중첩 I/O(Overlapped I/O)라고도 불림) -
동기 파일 I/O에서 스레드는 I/O 작업을 시작하고 I/O 요청이 완료될 때까지 즉시 대기 상태에 들어감
-
비동기 파일 I/O를 수행하는 스레드는 적절한 함수를 호출하여 커널에 I/O 요청을 보냄
-
요청이 커널에 의해 수락되면 호출 스레드는 커널이 스레드에 I/O 작업이 완료되었음을 알릴 때까지 다른 작업을 계속 처리
-
I/O 요청이 많은 시간이 걸릴 것으로 예상되는 상황에서 비동기 I/O는 일반적으로 처리 효율성을 최적화하는 좋은 방법
(예: 대규모 데이터베이스의 새로 고침 또는 백업 또는 느린 통신 링크) -
빠른 I/O 작업의 경우 커널 I/O 요청과 커널 신호를 처리하는 오버헤드로 인해 비동기 I/O가 덜 유익할 수 있으며, 특히 많은 빠른 I/O 작업을 해야 하는 경우 더욱 그럼
커널 (kernel)
-
커널은 운영체제 중 항상 메모리에 올라가 있는 운영체제의 핵심 부분
-
하드웨어와 응용 프로그램 사이에서 인터페이스를 제공하는 역할을 하며 컴퓨터 자원들을 관리하는 역할
-
스스로 실행되는 프로세스가 아니라 호출되어 실행되어지는 단순한 함수/데이터의 집합
스레드 (Thread)
-
스레드란 프로그램(프로세스) 실행의 단위이며, 하나의 프로세스는 여러개의 스레드로 구성이 가능
-
하나의 프로세스를 구성하는 스레드들은 프로세스에 할당된 메모리, 자원 등을 공유함
-
프로세스와 같이 실행, 준비, 대기 등의 실행 상태를 가지며, 실행 상태가 변할때마다 스레드 문맥교환(context switching)을 수행함
-
스레드별로 자신만의 스택과 레지스터를 가짐
스레드 풀링(Thread Pooling)
-
스레드 풀은 애플리케이션의 비동기 작업을 효과적으로 실행하기 위한 스레드 모음이며, 스레드 풀의 이러한 스레드는 작업자(Worker) 스레드라 칭함
-
스레드 풀링이란 할당된 일을 마친 스레드를 소멸시키지 않고, 스레드 풀에 저장해 뒀다가 필요할 때 다시 꺼내 쓰는 개념
( 스레드의 생성과 소멸은 시스템에 많은 부담을 줌 ) -
애플리케이션은 스레드 풀을 사용하여 애플리케이션 스레드 수를 줄이고 작업자 스레드를 관리함
-
작업 처리에 사용되는 스레드를 제한된 개수만큼 정해 놓고 작업 대기열(Task Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 것
문맥 교환(Context Switching)
-
프로그래밍에서 문맥(Context)는 (동작, 작업들의 집합)을 (정의, 관리, 실행)하도록 하는 (최소한의 상태, 재료, 속성)을 포함하는 (객체, 구조체, 정보)를 뜻함
-
프로세스 또는 스레드 의 상태를 저장하여 나중에 복원하고 실행을 재개할 수 있도록 한 다음, 이전에 저장된 다른 상태를 복원하는 작업으로, 이를 통해 여러 프로세스가 단일 중앙 처리 장치 (CPU)를 공유할 수 있음
-
한 프로세스에서 다른 프로세스로 전환하려면 레지스터와 메모리 맵을 저장하고 로드하고,
다양한 테이블과 목록을 업데이트하는 등 관리를 수행하는 데 일정 시간이 필요함
IOCP(Input/Ouptput Completion Port)
-
Windows NT 버전 3.5 이상, AIX 및 Solaris 10 이상의 운영 체제 에서 지원하는 기능
-
소켓이나 파일의 입출력(I/O)을 최소한의 스레드를 사용해서 처리하는 기법
-
IOCP는 스레드 풀링(Thread Pooling) + 비동기 I/O(=Overlapped I/O)와 같은 개념들을 이용해서 작동
-
커널이 요청한 Overlapped(비동기) 작업의 완료를 실시간으로 알려주는 대신,
작업 결과를 IOCP 대기열(Queue)에 넣고 나중에 사용자가 지정한 방법으로 통지하는 방식
사용 이유
-
작업을 스레드에 분산하여 최신 멀티프로세서 호스트를 활용가능
-
I/O 작업 처리를 효과적으로 예약함으로써 응답성과 처리량을 극대화
-
비동기 소켓 프로그래밍에서 효율적인 작업 처리를 위해 사용
-
대규모 서버 애플리케이션에서 성능과 확장성을 확보하기 위해 흔히 사용
기능
-
Windows OS가 직접 효율적인 스레드 풀링 제공으로 Context Switching을 줄임
-
애플리케이션 스레드 수를 줄이고 작업자(Worker) 스레드를 관리하는 데 사용
-
I/O 서비스를 요청하는 프로세스는 I/O 서비스 완료 알림을 받지 않고 대신 I/O 완료 포트의 메시지 대기열을 확인하여 I/O 요청 상태를 확인
-
커널 차원에서 I/O 처리를 스케줄링( 프로세스들에게 CPU 등의 자원을 적절히 배정 )하여 CPU 사용량을 최적화
( 스레드 간 작업 전환 시의 발생하는 Context Switching에 대해서도 풀의 스레드를 상황에 맞춰 조절함으로 회피가능 ) -
CPU에 무리가 가는 스레드의 잦은 생성과 파괴 작업을 피하는 대신 상태만 활성화/비활성화함으로 오버헤드를 줄임
수행 과정
-
CreateIoCompletionPort 함수를 통해 I/O 완료 포트(IOCP)를 만들고 하나 이상의 파일 핸들을 해당 포트와 연결
( 파일 핸들은 디스크의 파일이나 모든 네트워크 엔드포인트 또는 TCP 소켓에 대한 읽기/쓰기를 나타냄 )- 변수 NumberOfConcurrentThreads 는 IOCP에서 동시에 실행해야 하는 비동기 I/O 스레드 수를 결정함
( 대기열에 대기 중인 완료 패킷이 있지만 포트가 동시성 한계에 도달하여 대기를 충족할 수 없는 경우가 가장 효율적
=> 가장 좋은 최대값은 컴퓨터의 CPU 수 (0으로 설정하면 자동으로 인식) )
- 변수 NumberOfConcurrentThreads 는 IOCP에서 동시에 실행해야 하는 비동기 I/O 스레드 수를 결정함
-
완료 포트(Completion Port)가 생성되면 운영 체제(OS)는 대기열(Queue)를 생성하고 모든 I/O 완료 패킷을 연결하여 대기시킴,
이를 통해 I/O 완료 패킷은 애플리케이션 작업자(Worker) 스레드를 할당받아 처리될 수 있음 -
I/O 작업이 완료되면 I/O 스레드(=작업자(Worker) 스레드)는 IOCP에서 PostQueuedCompletionStatus 함수를 호출하여,
I/O 완료 패킷을 연관된 대기열(선입선출(FIFO) 형식)로 이동하고 사용 가능한 스레드 풀로 다시 이동 -
애플리케이션 작업자(Worker) 스레드가 사용 가능해지면 GetQueuedCompletionStatus 함수를 호출하여 IOCP 대기열에서 대기된 I/O 완료 패킷을 가져오고 추가 작업을 계속 진행
-
I/O 완료 포트에서 실행이 차단된(=작업 대기중) 스레드는 기본적으로 LIFO(Last-in-first-out) 순서로 해제되며, 가장 오래된 I/O 완료 패킷의 정보가 해당 스레드에 전달됨
( 최근에 사용된 스레드는 CPU 캐시에 여전히 남아있을 가능성이 높음
=> Context Switching 오버헤드를 줄이고 전체 시스템 성능을 향상가능 ) -
더 이상 비동기 I/O 스레드가 실행되지 않고 파일 핸들이 없으면 IOCP가 시스템에서 해제됨
( 따라서 더 나은 성능을 위해 모든 I/O 작업 리소스를 제대로 닫아야 함 )
-
I/O 완료 포트 스레드: IOCP 스레드는 특정 IOCP에 대한 알림을 기다리며 비동기 작업이 완료될 때까지 차단됨
-
SomeAsync 메서드 호출 : 작업 스레드에서 await DoAsync()가 실행되면, 이 메서드는 IOCP에 “바인딩”되고 비동기 I/O 작업이 시작됨
( 이때 비동기 작업의 진행 상태를 추적하는 상태 머신이(State Machine)이 생성되고 Task가 반환됨 ) -
비동기 I/O 작업이 완료됨
-
I/O 완료 신호: I/O 작업이 완료되면 IOCP가 특정 알림를 보내 차단된 IOCP 스레드 중 하나를 깨워 작업을 계속 진행하게 함
-
상태 머신 실행: 신호를 받은 IOCP 스레드는 상태 머신을 실행하여 Task의 결과를 설정함
( 이 과정에서 후속 작업(continuation)을 결정함 )
대채 프로그램
Reference
C-sharpcorner
Microsoft
TooSlowException