삽질의 현장/- 윈도우 시스템

#029_WIndow_System_IPC_파이프(2)

shovelman 2015. 9. 30. 16:24


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

이번 시간에는 파이프 통신하는 방법에 대해서 알아보도록 하겠습니다.



첫번째 인자로 파이프 이름을 받는데요,

그 외에 읽고 쓰고를 가능하게 할지, 버퍼의 크기, 보안 속성등을 지정할 수 있습니다.

여기서 우리는 네번째 인자를 봅도록 하겠습니다.


네번째 인자인 nMaxInstance의 경우 파이프를 최대 만들 수 있는 개수를 지정합니다.

즉, 통신할 수 있는 파이프를 네번째 인자의 수 만큼 생성할 수 있게 지정하는 것입니다.


다섯번째, 여섯번째의 버퍼는 많은 곳에서 사용이 됩니다.

파일에, DB에... 입출력을 필요로 하는 함수에서 버퍼의 존재를 심심치 않게 확인할 수 있습니다.

아무튼... 다섯번째, 여섯번째의 버퍼는 입력 버퍼, 출력 버퍼의 사이즈를 명시하는 인자입니다.


자... 파이프를 해당 함수를 통해 생성을 했다면, 파이프를 이어줄 클라이언트들을 기다리게 됩니다.

클라이언트들이 올 때까지... 

즉, 접속 요청이 있는지 체크하는 대기 함수인 ConnectNamePipe 함수를 소개합니다.



이 함수는 대기함수입니다.

대기함수란, '목적이 다할 때 까지 대기하고 있겠다'는 뜻을 가진 함수입니다.


우리는 이런 대기함수를 API 초창기 부터 사용했었습니다.

기억 나시나요? WinMain 함수에서 사용하던 GetMessage() 함수 말입니다...

요놈은 메시지 큐에 메시지가 들어오는 것을 대기하는 놈이었습니다.

그런데, 일반적으로 대기 함수라고 부르지 않고 메시지 확인 함수라고 부릅니다....

메시지가 있는지 체크하고, 없으면 멈춰있는 것이지요...


아무튼... 이게 중요한게 아니지요...

어찌됬건, 클라이언트가 만들어지면 CreateFile 함수를 통해 서버에게 접속 요청을 합니다...

파이프 하나를 나와 연결하자 이런 것이지요....


이때 접속이 성공하게 되면, 파이프 인스턴스 하나가 생성되는 것입니다.

즉, 서버와 클라이언트간의 통신을 위해 관 하나가 생성되는 것이지요...



새로운 인스턴스를 생성하고 그 인스턴스를 누군가가 사용할 때 까지

대기하는 역할을 하는 함수입니다.


파이프에 연결이 되었다면,

아래 CreateThread를 통해 새로운 쓰레드가 하나 생성되는 것이 보입니다.


이와 같이 코딩을 한 이유는,

파이프를 생성하고, 최대 파이프가 찰 때까지 연결 시켜주는 기능을 하는 쓰레드 따로,

프로세스간 통신을 하고자하는 쓰레드 따로,

Message 발생시 메시지를 처리하는 쓰레드 따로 등등

동시 다발적인 처리를 위해 쓰레드를 사용한다는 것을 보여드리기 위해서 소개했습니다.


파이프를 가졌다는 것은 파이프의 핸들을 가진 놈과 통신할 수 있게 되었다는 뜻입니다.

이제 파이프와 통신을 하게 되었다면,

ReadFile, WirteFile 함수를 통해 신나게 프로세스간 통신이 진행되게 됩니다.




이 두 함수는 모두 Blocking 함수입니다.

데이터를 읽고 보내기 전까지 대기 상태로 놓이기 때문이지요...


지금은 대기 함수와 비슷하다고 생각하시면 되는데, 

대기 함수는 자기 목적이 다할 때 까지 대기 상태로 놓입니다.

따라서 두 함수를 Block 함수라고 부르는 것입니다.


쓰레드와 프로세스 상태에는 크게 세가지 상태가 있습니다.

Ready, Running, Blocking 상태... 이 세 상태가 핵심이지요...

대기 함수를 호출했을 때 , Sleep 함수를 호출 했을 때, I/O 작업을 호출 했을 때

모두 Blocking 상태가 되버립니다.


프로세스간 통신을 하며 주고 받기 위해서 버퍼를 사용합니다.

그런데 데이터를 받으려는데 버퍼가 꽉 차있으면 안되지요...

따라서 Blocking 상태가 되는데, Blocking이 되는 이유가 

버퍼를 입력받을 때 까지 기다리는, 비울 때 까지 기다리는 I/O 작업이기 때문이라고 하는 것이지요...


WaitNamedPipe 함수의 경우 

인스턴스가 남아도는지 확인하는 함수입니다.



서버가 파이프를 생성할 때 까지 무한적으로 대기하는 Blocking 함수이지요...

즉, 인스턴스가 만들어 질 수 있는 갯수가 빌 때 까지 Wait하는 놈으로,

인스턴스가 하나 이상 빌 때 까지, 파이프 인스턴스가 남았는지 체크하는 놈입니다...



일반적으로 서버와 클라이언트 관계에서 누가 먼저 종료되는 것이 맞을까요?

경우에 따라서 다르겠지만,

일반적으로 클라이언트가 종료되고 접속되어 있는 클라이언트 없을 경우

서버가 종료되는 것이 올바른 종료 과정이라고 할 수 있습니다.

반대의 경우처럼 서버가 먼저 종료되면 이를 우리는 '오류'라고 부르지요...

따라서 정상적으로 클라이언트가 먼저 죽습니다.


이 얘기는 왜 언급했을까요...

이 개념은 네트워크에서도 통하게되는 개념입니다.

아무리 네트워크에서 뭔일이 일어나더라도,

서버는 뭔일을 수행하거나, 클라이언트와 통신하기 전까지는 클라이언트가 죽은지 모르게됩니다.


따라서 통신과 관련된 클라이언트와 얘기하는 어떤 함수들은

반드시 클라이언트의 상태값과 오류 값을 얻을 수 있습니다.

이 상태값을 통해 서버를 종료할 지에 대해서 코드를 구현할 수 있겠지요...


모든 클라이언트가 떠나고 서버가 종료하게 된다면 우리는 FlushFileBuffers와 같은 함수를 사용하게 됩니다.



파이프를 해제하는 함수 중 하나인데, Flush 가 붙는 함수는 

'가져오고자 하는 데이터를 모두 가져오고, 보내고자 하는 데이터를 모두 보내는 기능'

을 가지고 있는 함수들입니다.

즉, 버퍼를 비우는 기능의 함수라는 뜻이지요...

그리고 비워지지 않을 때 까지 Block 상태로 되있는 것이구요...


또한 DisconnectNamedPipe 함수를 통해 접속 요청을 끊고,

CloseHandle을 통해 커널 오브젝트를 제거하게 됩니다.

정확히는 usage count를 감소하는 것이지요...



지금까지 파이프 통신에 사용되는 함수들과 기능들에 대해서 알아봤습니다.


이상 삽잡이였습니다!