삽질의 현장/- 네트워크 프로그래밍

#024_Window_Network_비동기 소켓 입출력 모델_Overlapped 모델 (2)

shovelman 2015. 10. 14. 15:56


안녕하세요 삽잡이입니다.


이번 시간에는 완료 루틴(Completion Routine)을 사용하는 

Overlapped 모델에 대해서 알아보고자 합니다.


이번 모델은 비동기 방식을 사용하는 I/O 작업을 전문 쓰레드에 맡깁니다.

이처럼 I/O 작업만을 전문적으로 받는 쓰레드가 여러대 있다고 한다면,


이전 모델들은 A, B 클라이언트들에서 데이터가 들어오면

A 클라이언트의 작업을 처리하고, B 클라이언트의 작업을 처리해줘야했었는데,

이 모델은 I/O 작업을 전문적으로 받는 쓰레드를 사용하기 때문에 

A, B 각각 동작하도록 한다 이겁니다...

결과적으로, I/O 작업을 쓰레드로 만들어 놓으면 여러 작업을 효율적으로 처리할 수 있게 됩니다.


이번 Overlapped 모델에서는 Completion Routine을 사용합니다.



이렇게 사용을 합니다.


해당 함수는 Callback 함수이자, 완료 함수입니다.

완료되면 불려지는 함수이죠..


중요한 것은 I/O 작업을 메인에서 하지 않는다는 것입니다.

이제 I/O 작업을 아예 전문적으로 한 쓰레드가 한다 이겁니다..


어디에서도 완료 함수(Completion Routine)를 부르는 곳이 없지만,

완료함수는 자동적으로 불려집니다.


그렇다면, 자동으로 불려지기 위해서는 어떻게 해야할까요?

우리가 이전부터 사용하던 WSAResv, WSASend 등 함수들의 마지막 인자들을 주목하시길 바랍니다.



이렇게 마지막 인자로 완료 함수를 넣습니다.

즉, 해당 예제 코드는 Recv가 완료되면 Completion Routine 함수를 불러달라는 의미를 가지고 있습니다.


이전에는, 완료가 되면 Overlapped 이벤트를 신호상태로 만들고,

loop를 돌면서 checking 하여 소켓 구분을 확인하고 이에 따른 처리를 했었습니다.

하지만 이제는 recv를 할 것이니, 

완료가 되면 Callback 함수인 Completion Roution 함수를 호출해달라고 구현되어있다 이겁니다.


그런데, 완료 처리는 어플리케이션이 해줘야합니다.

OS가 recv 쓰레드를 수행하고 있을 터인데 recv 작업이 완료되면, 

Callback 함수를 OS가 호출하도록 하게 될 때에...

즉! Completion Routine (편의상 완료 루틴, CR)이라는 함수를 

OS가 호출하면 어플리케이션측에서 알 수 있냐가 문제라고 말하는 것입니다.


CR을 호출해주는 이유는

'완료가 되었으니 완료 루틴 작업을 해라'~ 라는 행동을 

어플리케이션에서 해야한다 이겁니다.

그런데, 어플리케이션은 열심히 작업중인데,

OS가 CR을 호출하고 그냥 가버리면... 어플리케이션이 알겠습니까?

어떤 시점에 CR이 호출되었는지 어플리케이션은 모른다 이겁니다..


따라서 어플리케이션은 Accept() 함수를 통해 클라이언트와의 연결만을 신경을 씁니다.

그리고 I/O 작업을 수행하는 전문 쓰레드 (편의상 WorkerThread)가 

WSARecv 혹은 WSASend를 호출하고 

CR은 WorkerThread로 부터 호출된 작업이 끝날 때 호출이 되는 것입니다.

Recv나 Send 작업이 끝났다면 완료 루틴 작업을 호출하고 

WorkerThread는 다시 자기 일을 한다 이겁니다...


CR이 봤을 때에는 OS가 쓰레드를 실행하다고 밖에 생각을 못합니다.

즉, OS에서 만들어진 쓰레드가 업무를 다 하고 CR을 호출하기 때문에,

업무를 내린 WorkerThread는 연관성이 없다 이겁니다.


그래서 진짜 호출을 하는 것이 아니라, 호출을 할 준비를 하도록 만듭니다.

CR을 정확하게 실행경로로 따지면

WorkerThread 쓰레드 안에 있다고 생각하면 된다 이겁니다.


즉, CR을 Recv나 Send작업이 완료되었을 때 실행하도록 한다는 것입니다.

그런데! Recvm Send 작업이 완료되었다면 

어플리케이션에 APC(Async Procedure Call, 비 동기적으로 호출되는 함수에 대한 큐)라는 

큐에 완료 정보를 보관하는 것입니다.

그렇다면, 각 쓰레드가 완료시 완료 정보를 APC에 보관하게 되겠군요...


자... 완료 정보가 다음에 CR에 전달되게 됩니다.

그런데, 이 완료 정보가 전달되는 시점은 어플리케이션에서 결정하게 되는 것입니다.

왜냐하면, WorkerThread 어플리케이션이 CR을 호출할 수 있을 때

그 시점을 어플리케이션이 결정하게 하려고 만들었기 때문입니다.


그래서 OS는 작업이 완료가 되면 APC 큐에 보관하는 일만을 담당하게됩니다.

그렇다면 APC 큐에 있는 정보를 가지고 CR함수를 호출하는 기능은 어플리케이션이 만들게 되겠지요...

이런 메커니즘이 필요하다 이겁니다.


Alertable State (편의상 AS) 즉, 알림 가능한 상태라는 의미를 아십니까?

이 의미는 'CR 실행 타이밍을 어플리케이션이 결정'할 수 있게 만들어주는 상태입니다.

AS는 알림을 받을 수 있다면 TRUE, 받지 못한다면 FALSE로 처리하면 됩니다.


모든 쓰레드에는 AS 상태가 있습니다.

즉, 알림을 받을 수 있거나, 없는 상태를 나타낼 수 있다 이겁니다.

만약, 이 상태를 TRUE로 만들면 간단하게 

APC 큐에 완료 정보가 있는지를 확인한다고 생각하시면 됩니다.

확인이 되면 정보를 꺼내서 CR을 호출하는 역할을 하는 것입니다.


이 쓰레드를 언제 AS 상태로 만들어줄지가 굉장히 중요합니다.

이 알림 가능한 상태로 만들어주는 함수에는 대표적으로 SleepEx() 함수가 있습니다.

AS 가능한 상태로 바꿀 수 있는 기능이 있지요...

Sleep() 함수는 그냥 Block 상태로 만들어주지만, SleepEx() 함수는 AS 가능한 상태로 바꿔주는 것입니다.


해당함수 이외에도 WaitForSingleObjectEx() 함수도 있습니다.



사실, Ex가 붙는 함수들은 한가지 인수들을 더 가지고 있습니다.

바로, Alertable State 상태 값입니다.


지금까지 WaitForSingleObject() 함수는 커널 오브젝트들이 

신호상태가 될 때 까지 기다리는 함수였는데,

이제 기능이 추가 됬습니다...

즉, 핸들의 커널 오브젝트가 신호 상태가 될 때 까지 기다리는 기능

혹은, Alertable State 상태를 알림 가능한 상태로 만들어 주거나 입니다...



정리하자면, Ex 함수는 신호 상태만이 되기를 기다릴뿐 아니라 

쓰레드를 신호 알림을 받을 수 있는 상태로 만들어준다 이겁니다.

즉, '나는 완료 보고를 받을 수 있는 쓰레드다!'를 나타내준다 이거죠...


쓰레드가 작업을 하고 있는데 CR이 호출되면,

쓰레드는 자신의 작업을 먼저 해야할까요? 아니면 CR 호출을 먼저 해야할까요?

경우에 따라서 다르기 때문에 정답은 우선 알 수 없습니다.

따라서 작업을 할 때에는 CR이 호출되면 안됩니다.


그런데, 일을 수행하다가... 만약, WaitForSingleObjectEx() 에서 Blocking 상태로 대기를 하고 있다면,

어플리케이션이 하는 일을 다하고 쉬고 있다는 뜻이기 때문에...

즉, 쓰레드가 쉬고 있다는 소리로 CR이 호출되도 상관 없다는 상태가 된다는 소리 아니겠습니까...

쓰레드가 완료 보고를 받을 준비가 되어있다는 소리입니다.

따라서 쉬고 있되, 알람 가능한 상태일 때 CR을 받게 해야합니다.



그렇다면, WaitForSingleObjectEx() 함수가 return이 되는 경우는 세가지 이겠군요...

첫번째, 오류가 발생했을 때...

두번째, 이벤트가 신호 상태가 될 때...

세번째, 알림 가능한 상태로 만들고 나서 CR이 수행되었을 때...

즉, 목적은 두 가지입니다.

CR 호출이 목적이고, 이벤트가 신호상태가 됬는지 확인하는 것이 목적이죠...

사실, 이 두 가지 때문에 reutrn이 된다 이소리입니다.


이전에도 말씀드렸지만,

비 동기 입출력 모델들은 다 똑같은데 알림 보고만이 다를 뿐입니다...


이번 모델도 또한 더미 이벤트가 필요합니다.

클라이언트가 들어왔을 때 마다 이 더미 이벤트를 신호 상태로 만들어줘서

신호 상태를 살펴보는 대수를 갱신 시켜주고,

쓰레드가 AS 상태로 빠지려면 WaitForSingleObjectEx()와 같은 함수들이 필요한데,

해당 함수를 호출하려면 적어도 인수 하나를 가지고 있어야 되기 때문입니다.


AS 가능한 상태에서 완료보고가 올라오면 완료 보고 정보를 꺼내와 CR을 수행하고 return 하는!

즉, CR이 return 하면 WaitForSingleObject가 return을 합니다...

WorkThread가 CR을 호출하는 것이지요...


오늘은 여기까지 하겠습니다.

이상 삽잡이였습니다!