정보공간_1

[7기 대구 유용수] IO Completion Port 3장. 서버모델과 IOCP개념 본문

IT 놀이터/Elite Member Tech & Talk

[7기 대구 유용수] IO Completion Port 3장. 서버모델과 IOCP개념

알 수 없는 사용자 2015. 5. 25. 17:47

 앞장에서 네트워크,TCP/IP,winsock까지 알아보았습니다. 이제는 IOCP를 본격적으로 알아볼텐데요, 그 전에 먼저 알아야 할 용어들과 내용들이 있습니다. 이 개념을 숙지 하고 난 후에 IOCP를 제대로 작성해 보겠습니다.

블로킹과 논블로킹(Blocking I/O, nonBlocking I/O)

 소켓 프로그래밍을 하면서 듣는 얘기중 블로킹 소켓(Blocking Socket), 넌블로킹 소켓(nonBlocking Socket)등을 들어 보셨을 겁니다. 

 위에 말 그대로 블로킹이나, 논블로킹 소켓이라함은 그 소켓 함수중에 작업이 중된다는 상황이 있는 함수냐 아니냐로 나뉘게 됩니다. 블로킹이 된다면 작업이 호출되기까지 블로킹 함수에서 하염없이 대기를 하게되는 불편함이 있고, 논블로킹은 다른작업을 수행할 수 있지만 설계가 복잡해지는 단점이 있습니다.

 예를 들어 read(), write() 함수들을 살펴보면 일반적인 블로킹 모드에서는 클라이언트에서 write()를 호출하기 전까지 서버에서는 read()가 무한대기하게 됩니다. 하지만 논블로킹 모드에서는 read()에서 읽을 데이터가 있으면 읽고, 없으면 대기하지 않고 넘어가게 됩니다. 하지만 이러한 방법도 소켓에 읽을 데이터가 있는지 loop돌면서 계속 호출하여야 하여야 합니다.

블로킹, 논블로킹 정리

여기까지 블로킹, 논블로킹 에 대해서 알아 봤습니다. 블로킹과 논블로킹의 차이점은 파일디스크립터 관련 함수에서 기다리냐 마느냐의 차이점입니다. 논블로킹에서 read()는 읽을 데이터가 없을 시 에러(-1)을 return을 하게 되어 경우에 따라서 적절한 처리를 해주어야 하기 때문에 프로그래밍이 조금 더 복잡해 질 수도 있습니다. 하지만 에러처리를 잘하면 어떠한 빈틈도 없는 멋진 프로그램이 될 수도 있다는 뜻입니다.

동기와 비동기(Synchronous I/O, Asyncronous I/O)

 이제 소켓프로그래밍중에 또 많이듣게되는 동기 입출력, 비동기 입출력에 대해서 알아보겠습니다. 

 사실상 동기는 blocking과 차이점이 없다고 보시면 됩니다. 결국 특정 요청이 왔을때, 요청의 결과가 오기까지 wait를 하는 상태이기때문입니다. 하지만 비동기는 nonBlocking과 내부적으로 작동하는 방법에 차이가 있습니다.

 앞서 설명들인 nonBlocking에서는 read()에서 처리할 내용이 없으면 다음 절차로 넘어가서 진행된다고 말씀드렸었는데, 비동기에서는 read()에서 바로 return이 됩니다. 비동기 함수를 호출 할 때는 작업 완료에대한 event나 callback함수를 설정하게 됩니다.

위 그림과 같이 aio_read를 호출하고 바로 return을 받게되고, 커널은 데이터가 들어왔을때 event혹은 callback함수를 호출하게 됩니다. 즉 nonBlocking처럼 지속적인 검사가 필요한게 아니라, 일단 작업을 걸어 두고 다른 작업을 진행하다가 특정 신호에 따라서 걸어둔 작업을 진행하는 방식입니다.

서버 종류

 이러한 특성들로 서버에는 방식에 따른 종류가 나뉘어 집니다. 큰 카테고리로는 반복서버와 병행서버로 나누어 지는데 이에대한 개념은 이렇습니다.

이 뜻이 무엇인가 하면 반복서버는 접속한 여러개의 Client들을 하나씩 차례대로 처리는 한다는 의미이고,  병행서버는 접속한 여러 클라이언트를 병렬적으로 처리한다는 의미입니다.

 그렇다면 이상적인 서버모델은 어떻게 이루어 져야 할까요? 개념적으로는 이 사항들을 잘 고려해서 서버를 작성 한다면 이상적인 서버흐름이 진행될 수 있습니다.

서버 모델

 이러한 사항들을 고려해서 어느정도 일반화된 서버모델들이 있습니다. windows환경에서는 대표적으로 Select Model, Overlapped IO Model, IO Completion Port Model이 있습니다.

1. Select Model

 Select Model은 말 그대로 select()함수가 가장 핵심적인 역할을 하기 때문에 붙인 모델명으로써, 해당 모델의 장점은 하나의 Thread로 여러개의 socket을 처리할 수 있다는 점입니다. 우리의 목적은 IOCP를 활용하는 것 이기 때문에 해당 모델에 대해서는 입출력 절차의 흐름만 인지하고 넘어가도록 하겠습니다.


2. Overlapped IO Model

 해당 모델명은 말 그래도 입출력을 중첩시켜서 처리하기 때문에 붙여진 이름입니다. 말 그대로 IO를 중첩시켜 하나의 Thread에서 여러개의 IO처리를 가능하게 하고, 작업 완료에 대한 통지를 비동기적으로 확인 가능합니다. IOCP모델의 기반이 되는 모델이 바로 중첩입출력 모델이기에 개념을 잘 짚고 넘어가야 합니다.

그렇다면 어떤 식으로 IO를 중첩해서 사용하는 걸까요? 일단 해당 모델을 묘사한 그림을 한번 보겠습니다.

그림과 같이 시간에 흐름에 따라 특정 입출력의 시작과 완료 후에 다른 입출력이 되는 것 이 아니라, 여러개의 입출력이 시간의 흐름에따라 중첩으로 시작되고 완료된다는 것 입니다. 이때 내부적으로 입출력 버퍼는 커널모드에서 진행되며, 작업이 완료되었을때, 완료 루틴만 유저모드로 전달 하기에 모드 변화에 대한 부하가 적습니다. 이러한 작업들을 위해서 중첩입출력 모델에서는 특정 소켓과,  특정 구조체, 특정 버퍼를 사용 하여야 합니다. 해당 함수와 구조체의 설명은 IOCP와 중복되기에 뒤에서 설명 하도록 하겠습니다. 

 그렇다면 왜 중첩입출력 모델에서 IOCP로 또 달라진 것 일까요? 그것은 event모드에서는 최대 연결 갯수가 64개로 제한되어 있기 때문이고, 이것을 극복한 callback모드에서는 결국 callback함수 호출을 많이 하기 되면 수천개의 대규모의 Client들을 관리 하는대에 문제가 있기 때문입니다. 

3. IOCP 모델

 IOCP란 중첩입출력 모델의 확장이며, 여기서의 Port는 네트워크상의 포트가 아닌, 작업 혹은 서비스를 전담하기 위해 만들어지는 객체(socket이라고 판단)입니다. 먼저 모델의 묘사도를 보시겠습니다.

입출력 장치로 부터 입출력이 완료되면, 이 완료 보고는 완료대기열(Completion Queue)에 쌓이게 됩니다. 이때 해당 큐에 보고가 있을시에 수면중이면 Thread가 대기열의 보고를 읽어서 데이터를 처리하는 방식입니다. 

 이런 방식의 장점은 Client의 접속시마다 Thread를 할당하는 멀티 스레드 방식과 달리, 미리 쓰레드를 만들어서 만들어 놓은 소수의 워커 스레드로 작업을 진행합니다. 이런 방식은 스레드풀 혹은 프로세스 풀과 비슷해 보이지만, 일반적으로 풀을 사용시에 스레드에 작업을 할당하기에 수월하지 않습니다. 이를 위해 몇가지의 까다로운 기법을 사용해야 하는데, IOCP는 운영체제가 알아서 스레드의 동작과 휴식을 관리합니다. 이런 방식의 가장큰 이점은 소수의 Thread와 운영체제 내부에서 관리로 context-switching의 최소하가 가장 큰 이점 입니다.

 다음으로 IOCP모델의 입출력 절차를 보시겠습니다.

1. 워커스레드를 생성합니다(적당한 갯수)

2. 소켓 생성

3. accept함수 호출

4. 연결되면, 소켓을 Completion Port에 할당

5. WSARecv()함수 호출

 순으로 코드를 짜면 됩니다. 여기서 만든 워커스레드는 GetQueuedCompletionStatus()함수에서 완료통보를 기다리고, 이 함수는 OVERLAPPED구조체를 반환합니다. 해당 구조체를 토대로 데이터를 유저모드에서 처리하면 됩니다.

 그 전에 먼저 GetQueuedCompletionStatus()함수와 연결을 위해서 CreateIoCompletionPort()함수를 히용해서 소켓에 HANDLE을 할당 하여야 합니다. 이제 다시 위의 절차를 보시면 이해가 되실 겁니다.

 이 까지가 IOCP코드를 작성하기 위해 우리가 선행해야 할 개념과 내용들이었습니다. 이제 다음장에서 해당 구조체와 함수, 그리고 실제 코드를 배워보도록 하겠습니다.