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

#027_WIndow_System_IPC_메시지

shovelman 2015. 9. 28. 16:04


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


여러개의 프로세스들은 각각 독립적으로만 움직일까요?

아닙니다. 프로세스 간에 통신을 통해 서로 대화를 주고받을 수 있습니다.

그래서 이번 시간에는 프로세스간에 통신의 방법 중 하나인 메시지에 대해서 알아보려고합니다.


IPC는 Inter Process Communication 의 줄임말입니다.

즉, 프로세스들 사이에 이야기를 하겠다는 뜻입니다.

그렇다면 윈도우 시스템 프로그래밍의 IPC에 관련된 기술들을 살펴보도록하죠...


이전에 FindWindow 함수를 통해 프로세스 ID 혹은, 클래스 명을 알고 있다면 

다른 프로세스의 핸들을 구할 수 있다는 사실을 확인하셨던 것 혹시 기억하십니까?

왜 지금 이런 말을 언급하냐 하면, 

핸들을 알고 있을 경우 다른 프로세스에게 통신이 가능할 수 있게 되기 때문입니다.


A 라는 프로세스에 윈도우가 있고, B 프로세스에 윈도우가 있다고 해봅시다.

A프로세스와 B 프로세스가 통신을 하고자 한다면 어떻게 해야할까요? 

통신을 하고자 하는 프로세스의 핸들을 구하고 

SendMessage 함수를 통해 다른 프로세스에게 메시지를 보낼 수 있겠군요...


그런데, 자기 프로세스 내에서는 최대 8바이트정도면(wParam + lParam) 모든 데이터를 날릴 수 있는데...

즉, 내 자신 안에서 주소가 달라질 일이 없으니, 

시작 주소만 알려주면 주소를 이용해서 데이터를 찾을 수 있었습니다.


하지만, 다른 프로세스에 내 프로세스의 주소를 보내봤자 소용이 없다는 사실을 알고 계십니까?

왜냐하면, 독립적인 주소 공간을 사용하기 때문입니다.

각 프로세스별로 예를 들어 0 부터 0xFFFFFFF와 같이 

독립적인 주소 공간을 가지고 놀기 때문이라는 것입니다.


정리하자면,

내 프로세스내에서는 8바이트만 있어도 모든 데이터를 날릴 수 있는 반면에,

다른 프로세스에 주소를 날리는 경우에는

프로세스간에는 독립적인 주소 공간을 사용하기 때문에 무용지물이 된다는 것이죠...



우리는 지금 메시지를 통한 프로세스간의 통신을 하고자 하는 목적이 있습니다.

그런데, 다른 프로세스에게 최대 8바이트까지 보낼 수 있는 한계로부터 

내가 보내고자 하는 모든 것을 보내기에는 부족함이 흘러 넘칠 것 같지 않습니까?


그래서 Windows에서는 WM_COPYDATA 메시지를 제공해줍니다.

즉, 더 많은 데이터를 날리고 싶어서 만들어진 특수한 메시지가 WM_COPYDATA라는 것입니다.

이 메시지는 OS가 중간에 개입하게 됩니다.

핵심은 WM_COPYDATA라는 메시지 인데, 이 메시지에는 세가지의 정보가 들어간다는 사실에 주목해야합니다.


이 세가지 정보를 위해 제공해주는 Copy Data Structure를 우선 살펴보시겠습니다.



요렇게 생겨먹었습니다...

안에는 세가지의 인수가 들어가있는데요, 총 12바이트입니다.


더 많은 데이터를 날리고 싶어 가져온 이 구조체에

첫번째 인자는 전달 될 숫자 값입니다. 

4바이트짜리 메시지만을 날려도 상관이 없다면 요놈만 사용해도 됩니다. 

아무튼... 보통 데이터의 종류를 구분하기 위해서 사용됩니다.


그리고 두번째 인자는 Count Byte Data로써,

세번째 인자인 lpData와 같이 콜라보를 이루는 인자입니다.

아무튼... 두번째 인자는 실제 전달할 데이터의 크기를 나타냅니다.


그리고, 우리가 보내고자 할 데이터가 담겨져 있는 시작 주소를

세번째 인자인 lpData에 담으면 됩니다.

예를 들어 lpData로 날리고자 하는 양이 1KB라면, 

cbData는 그 메모리의 양을 써주면 된다는 것입니다.


메시지가 하나 날려올 때 부가정보를 lParam과 wParam으로 같이 날라오는 것은

지금까지 API에 대해서 배우셨다면 모두가 아는 사실입니다.

따라서, 우리는 lParam에는 COPYDATA의 시작 주소를...

wParam에는 COPYDATA를 보낸 놈의 핸들을 같이 보내도록 하겠습니다.


지켜야할 약속은, lParam으로 COPYDATA 구조체의 주소를 보내야된다는 것입니다.

참고로 wParam은 일반적으로 핸들을 보내는 것이라고 합니다.


아무튼... 

WM_COPYDATA 메시지를 받은 프로세스는 

wParam과 lParam으로 받은 데이터를 전달 받습니다.

그런데, 전달 받은 데이터는 모두 보낸 프로세스의 주소를 가지고 있을 것입니다.


뭔말이냐, A프로세스가 B프로세스에게 보냈다고 해봅시다...

B프로세스가 받은 주소는 A프로세스의 변수로부터 나온 주소이니,

A프로세스의 주소를 가지고 있지 않겠습니까?


wParam으로 전달 된 핸들은 보장이 되지만,

lParam으로 전달 되어 온 시작 주소는 아까 위에서 언급했듯이 

프로세스마다 독립적인 주소 공간을 사용하는데 이 주소값이 보장이 되지 않을 것입니다.


예를 들어 0x12f32 라는 주소에 구조체에 대한 내용을 집어 넣고, B프로세스로 보냈는데

B프로세스의 0x12f32 주소에 이미 저장되어 있는 데이터들이 있으면 어떻게 합니까...


이 의문은 OS가 해결해 줍니다.

lParam에는 분명 구조체의 주소가 보내어지는데, 

재미있는 것은 통신을 통해 넘어온 A프로세스에 있던 내용물이 B프로세스에 똑같이 저장이 되고, 

주소만 B프로세스에 맞게 저장이 된다는 것입니다.

그걸 바로 OS가 넣어주는 것이구요...


재미있지 않습니까?  (재미있습니다...)

구조체를 만들고 시작 주소를 넘기면, OS가 똑같은 내용물을 만들어주고 

주소를 lParam에서 날린 것 처럼 반환해준다는 것입니다.


이게 왜 가능하냐면 말입니다...

A나 B프로세스는 물리 메모리에 매핑되어 있는데, (그 안에는 실제 주소를 가리키고 있다는 소리입니다.)

A프로세스의 주소가 다시 B프로세스의 메모리에 매핑된다는 것입니다.


뭔말이냐하면...

프로세스에 저장되어 있는 메모리는 가상 메모리로써 해당 프로세스만 사용할 수 있는 주소인데,

OS가 넘겨받은 내용물을 똑같이 메모리에 만들어준다는 것입니다.


따라서 OS의 도움으로 인해 전달 받은 데이터를 사용할 수 있게 되지만,

이는 OS가 만들어준 데이터이다보니 내가 만든 메모리처럼 사용할 수 없습니다.

즉, OS가 만들어준 메모리이니 허용된 공간인 WM_COPYDATA 메시지가 종료되면 

그 메모리는 어떻게 될지 모른다 이겁니다...


정리하자면, 

내가 만든 메모리가 아니라 내 주소 공간에 OS가 만들어준 메모리라는 것입니다.

그리고 OS가 만들어준 데이터 공간의 주소를 넘겨준 것 뿐이고,

유요한 곳은 WM_COPYDATA 메시지 내에서라는 것입니다.

return이 되는 순간 그 메모리 공간은 OS가 가져가게 됩니다.



지금까지 IPC 기법 중 메시지... 그 중에서도 WM_COPYDATA 메시지에 대해서 알아봤습니다.

이상 삽잡이였습니다!