윈도우즈 시스템 프로그래밍

윈도우즈 시스템 프로그래밍 7장 - 프로세스간 통신(IPC)

111-000-111 2021. 7. 25. 16:45

 

 

 

윈도우즈 시스템 프로그래밍이라는 책과 해당 책의 저자이신 윤성우님의 강의를 통해 공부한 내용을 정리하는 글입니다.

 

 


 

 

 

프로세스간 통신(IPC : Inte-Process Communication)의 의미

 

IPC는 프로세스 사이의 통신이라는 뜻으로, 둘 이상의 프로세스가 데이터를 주고 받는 행위이다.

 

 

⦁ 프로세스 사이에서 통신이 이뤄지기 위한 조건

 

서로 통신하고자 하는 프로세스가 서로 공유하는 메모리 영역이 존재한다면 프로세스간 통신은 아주 쉬워지지만, 여건이 그렇지 않으므로 보조 수단이 필요하다.

 

 

⦁ 프로세스들이 서로 만날 수 없는 이유

 

프로세스들은 안전성 때문에 자신에게 할당된 메모리 공간 이외에는 접근이 불가능하다.

A 프로세스는 자신의 메모리 공간 이외에는 접근이 불가능하며, B 프로세스도 마찬가지이다.

그렇기 때문에 통신수단이 필요하다.

 

프로세스별 독립적 메모리 공간(코드 영역 생략)

 

 

 

 

 

 

 

메일슬롯 방식의 IPC

 

 

⦁ 메일슬롯의 원리

 

데이터를 전달하고자 하는 프로세스를 Sender, 수신하는 프로세스를 Receiver라고 하자.

Receiver는 메일슬롯을 가지고 있고, Sender는 Receiver의 주소를 통해서 Receiver의 메일슬롯을 향해 데이터를 전송한다. 그러면 Receiver는 메일슬롯을 통해서 데이터를 얻게 된다.

 

 

 

⦁ 메일슬롯 구성을 위해 필요한 요소

 

① Receiver가 준비해야 할 요소

Receiver 프로세스는 데이터를 전송받기 위해서 메일슬롯을 생성해야한다. 해당하는 함수는 아래와 같다.

HANDLE CreateMailslot (
    LPCTSTR lpName,
    DWORD nMaxMessageSize,
    DWORD lReadTimeout,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
//If the function fails, the return value is INVALID_HANDLE_VALUE

 

 매개변수 설명

⦁ lpName : 메일슬롯의 이름을 결정하는데 사용됨, 주소를 지정하는 것이다.

⦁ nMaxMessageSize : 메일슬롯의 버퍼 크기 지정. 0이 전달되면 시스템이 허용하는 최대 크기로 설정된다.

⦁ lReadTimeout : 전송된 데이터를 읽기 위해 ReadFile 함수가 사용된다. 만약 메일슬롯이 비어있다면 채워질 때까지 ReadFile 함수는 반환하지 않고 블로킹 상태가 된다. lReadTimeout은 최대 블로킹 시간을 밀리세컨드로 단위로 지정하는데 사용된다. 0을 전달하면 데이터 유무에 관계없이 다음 단계를 실행한다.
상수 MAILSLOT_WAIT_FOREVER를 인자로 전달하면 읽어 들일 데이터가 존재할 때까지 블로킹 상태가 된다.

⦁ lpSecurityAttributes : 핸들을 상속하기 위한 용도로 사용된다.

 

 

 

 

② Sender가 준비해야 할 요소

 

Sender는 Receiver가 만들어 놓은 메일슬롯의 이름을 알아야 데이터를 보낼 수 있다.

메일슬롯은 Windows 파일 시스템을 기반으로 구현되어 있다. 그렇기 떄문에 입출력 함수를 사용하여 구현한다.

 

1단계 : CreateFile로 데이터를 전송할 통로 생성

2단계 : WriteFile로 데이터를 전송하는 작업

 

CreateFile에 인자로 들어갈 메일슬롯의 주소 체계
\\computername\mailslot\[path]name

실질적으로 채워야 할 부분은 "computername", "[path]name" 부분이다. 로컬 컴퓨터는 "computername" 부분에 '.'을 넣으면 된다.
[path]name은 실질적인 메일슬롯의 이름이다.

 

 

프로그래밍 모델

 

 

 

메일슬롯 예제

 

 

 MailReceiver.cpp

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")

int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hMailSlot;
	TCHAR messageBox[50];
	DWORD bytesRead;

	hMailSlot = CreateMailslot(SLOT_NAME, 0, MAILSLOT_WAIT_FOREVER, NULL);

	if (hMailSlot == INVALID_HANDLE_VALUE)
	{
		_fputts(_T("Unable to create mailslot!\n"), stdout);
		return 1;
	}

	_fputts(_T("******* Message *******\n"), stdout);
	while (1)
	{
		if (!ReadFile(hMailSlot, messageBox, sizeof(TCHAR) * 50, &bytesRead, NULL))
		{
			_fputts(_T("Unable to read!"), stdout);
			CloseHandle(hMailSlot);
			return 1;
		}

		if (!_tcsncmp(messageBox, _T("exit"), 4))
		{
			_fputts(_T("Good Bye!"), stdout);
			break;
		}

		messageBox[bytesRead / sizeof(TCHAR)] = 0;
		_fputts(messageBox, stdout);
	}

	CloseHandle(hMailSlot);
	return 0;
}

 

메일슬롯을 생성해서 메일슬롯에 들어오는 데이터를 읽어서 콘솔에 출력하는 프로그램이다.

 

 

 

 

⦁ MailSender.cpp

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")

int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hMailSlot;
	TCHAR message[50];
	DWORD bytesWritten;

	hMailSlot = CreateFile(SLOT_NAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hMailSlot == INVALID_HANDLE_VALUE)
	{
		_fputts(_T("Unable to create mailslot!\n"), stdout);
		return 1;
	}

	while (1)
	{
		_fputts(_T("MY CMD>"), stdout);
		_fgetts(message, sizeof(message) / sizeof(TCHAR), stdin);

		if (!WriteFile(hMailSlot, message, _tcslen(message) * sizeof(TCHAR), &bytesWritten, NULL))
		{
			_fputts(_T("Unable to write!"), stdout);
			CloseHandle(hMailSlot);
			return 1;
		}

		if (!_tcscmp(message, _T("exit")))
		{

			_fputts(_T("Good Bye!"), stdout);
			break;
		}
	}
	CloseHandle(hMailSlot);
	return 0;
}

 

지정한 메일슬롯에 데이터를 전송하는 프로그램이다.

 

CreateFile 함수의 첫 번째 전달 인자는 데이터를 전달할 메일슬롯을 지정하는 용도이다.

두 번째 전달인자는 파일의 개방 모드가 읽기 모드인지 쓰기 모드인지, 읽기/쓰기 모드인지 지정하는 용도이다.

다섯 번째 전달인자는 파일의 생성방식을 결정짓는다. 새로운 파일을 생성할 것인지, 기존의 파일을 열어서 접근할 것인지를 결정한다.

 

 

WriteFile 함수

BOOL WriteFile (
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
);
//If the function fails, the return value is zero.
⦁ hFile : 데이터를 읽어들일 메일슬롯 지정
⦁ lpBuffer : 전송할 데이터가 저장된 버퍼 지정
⦁ nNumberOfBytesToWrite : 전송할 데이터 크기 지정
⦁ lpNumberOfBytesWritten : 함수 호출 완료 후 전송된 실제 데이터의 크기를 바이트 단위로 얻기 위한 변수의 주소 지정
⦁ lpOverlapped : 일단 NULL 전달, 차후에 등장한다.

 

 

 

 

메일슬롯과 IPC

 

메일슬롯은 한쪽 방향으로만 메시지를 전달할 수 있다.

메일슬롯은 브로드캐스팅 방식의 통신을 지원한다. 즉, 하나의 Sender는 한번의 메시지 전송으로 여러 Receiver에게 동시에 전송 가능하다.

이러한 브로드캐스팅을 확인하고 싶으면, MailSender의 메일슬롯 주소 부분을 로컬 컴퓨터를 가리키는 "."에서 모든 컴퓨터를 의미하는 "*"로 변경해주면 네트워크로 연결된 컴퓨터들의 MailReceiver 프로세스에서 확인이 가능하다.

 

메일슬롯은 참조하는 프로세스가 메일슬롯을 생성한 프로세스 하나이므로 생성과 동시에 Usage Count가 1이 된다. 

자식 프로세스는 생성과 동시에 참조하는 프로세스가 둘이 되므로 2가 된다. 프로세스와 쓰레드를 제외한 다른 커널 오브젝트는 생성과 동시에 Usage Count가 1이 된다.