시스템 프로그래밍7부 - 프로세스간 통신(IPC) 1

김주현·2021년 10월 4일
0

시스템 프로그래밍

목록 보기
7/21

하나의 프로그램이 하나의 프로세스를 의미하는것은 아님 여러개의 프로세스로 구성될수도있음

프로세스간 통신(IPC)의 의미

IPC는 Inter_Process Communication의 약자로서 "프로세스 사이의 통신"이라는 뜻을 지닌다. 프로세스들이 서로 통신한다는 것은 무엇을 의미하는 것일까? 통신이란 기본적으로 데이터를 주고 받는 행위이다. 따라서 프로세스들이 서로 통신을 한다는 것은 둘 이상의 프로세스가 데이터를 주고 받는 행위라고 정의할 수 있다.

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

서로 통신하고자 하는 프로세스가 서로 만날 수 있는 여건(공유하는 메모리 영역이 존재)이 허락되면 프로세스 간 통신은 아주 쉬워진다. 그러나 프로세스들이 서로 만날 수 있는 여건이 허락되지 않는다면, 전화나 메신저와 같은 보조 수단이 필요하다.

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

결론부터 말하자면 프로세스들은 서로 만나서 데이터를 주고 받는 것이 불가능하다. 서로 데이터를 주고 받을만한 접선 장소를 마련할 수 없기 때문이다.

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

-프로세스들이 서로 만나지 못하게 디자인한 이유

프로세스는 자신에게 할당된 메모리 공간 이외에는 접근이 불가능하다 이렇게 제한을 가하는 이유는 안전성을 높이기 위한것임을 알아야한다.(막지 않을경우 프로세스가 다른 프로세스에게 영향을 미칠수있음)

메일슬롯 방식의 IPC

메일슬롯의 원리

메일슬롯은 파이프와 더불어 대표적인 IPC기법이다. 메일슬롯은 편지를 넣을수 있는 가느다란 우체통의 입구를 의미한다, 단방향의 특성을 가짐,동일한 주소를 가지는 메일슬롯을 여러개 만들수 있고 센더가 같은주소로 전송할경우 브로드캐스팅이 가능하다.

메일슬롯의 기본 원리 : "데이터를 주고 받기 위해서 프로세스가 우체통을 마련하는것."

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

첫째로 수신자가 준비해야 할것

수신자에 해당하는 프로세스는 메일슬롯 생성함수를 통해 메일슬롯을 생성해야한다.

HANDLE CreateMailSlot(
LPCTSTR lpName // 생성하는 메일슬롯의 이름을 결정(쉽게말해서 주소를 지정)
DWORD nMaxMessageSize //메일슬롯의 버퍼 크기를 지정하는데 사용 (0 : 시스템허용최대)
DWORD lReadTimeout // 메일슬롯을 통해 전송된 데이터를 읽기 위해서 파일 입 출력 함수인 ReadFile함수가 사용된다. 만약에 메일슬롯으로 부터 읽어 들일 데이터가 있다면,이 데이터들을 읽어 들이면서 ReadFile 함수를 빠져나오게 된다. 그러나 메일슬롯이 비어 있다면 데이터가 채워질 때까지 ReadFile 함수는 반환하지않고 블로킹 상태에 놓이게 된다.
lReadTimeout은 최대 블로킹 시간을 밀리세커드 단위로 지정하는 데 사용된다. 따라서 0을 인자로 전달하면 메일슬롯에 읽어 들일 데이터가 있든지,없든지 간에 블로킹 상태 없이 빠져나와서 다음단계를 실행하게 된다. 그리고 상수 MAILSLOT_WAIT_FOREVER를 인자로 전달할 경우 ReadFile 함수는 읽어 들일 데이터가 존재하게 될 때까지 블로킹 상태에 놓이게 된다.
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 핸들을 상속하기 위한 용도로도 사용된다.

반환값은 커널 오브젝트의 핸들이 반환된다. 메일슬롯도 커널에 의해서 관리되는 리소스 이기때문에 커널 오브젝트가 더불어 생성되고 ,이 커널 오브젝트의 핸들이 반환되는 것이다.

둘째로 Sender가 준비해야 할 것에 대해서 살펴보자

Sender는 데이터를 수신자에게 보내는 역할을 한다 따라서 송신자는 수신자가 만들어놓은 메일슬롯의 이름을 알아야한다.

메일슬롯은 분명 파일은 아니다. 그럼에도 불구하고 파일 입출력 함수를 사용해서 데이터를 주고 받는다. 이는 메일슬롯의 구현 원리에서 그 이유를 찾을 수 있다. 메일슬롯은 WIndows 파일 시스템을 기반으로 구현되어 있다. 때문에 메일슬롯을 기반으로 하는 CreateMailslot이나 WriteMailslot과 같은 이름의 함수를 만들 경우, 기능은 같고 이름만 다른 함수 여럿을 만들어 내는 꼴이 된다. 이것이 나쁘다는 것은 아니다. 다만 마이크로소프트에서는 이러한 형태의 함수들을 디자인하지 않고 기존에 정의되어 있는 파일 입 출력 함수를 사용하기로 결정했을 뿐이다.

주소 체계의 기본 골격 : \computername\mailslot[path]name

computername(.넣으면 이는 현재 사용하고 있는 로컬 컴퓨터를 의미) 부분과 name부분을 수정해야함.

[path]name은 실질적인 메일슬롯 이름이다. path정보를 포함해서 계층 구조의 형태로 보다 체계화할 수도 있다. "\.\mailslot\abc\def\ mailbox"

메일슬롯에 데이터를 전송하기 위해서 해당 메일슬롯의 연결을 의미하는 데이터스트림이 필요. 이는 CreateMailslot 함수의 호출을 통해서 생성되는 메일슬롯과는 또 다른 형태의 리소스로서, 이 역시도 운영체제에 의해서 커널 오브젝트와 핸들의 생성을 동반한다.

-메일슬롯의 예

ReadFile함수는 원래 파일로부터 데이터를 읽어 들일 때 사용되는 함수이지만 여기서는 메일슬롯으로부터 데이터를 읽어 들이기 위한 용도로 사용되고 있다.

BOOL ReadFile(
HANDLE hFile //메일슬롯의 핸들을 인자로 전달한다. 그러면 ReadFile 함수는 해당 메일슬롯에 존재하는 데이터를 읽어들인다.
LPVOID lpBuffer // 읽어 들인 데이터를 저장할 버퍼를 지정하는 용도로 사용된다.
DWORD nNumberOfBytesToRead // 읽어 들일 데이터의 최대 크기
LPDWORD lpNumberOfBytersRead // 함수 호출이 완료된 후에, 읽어 들인 데이터 크기를 바이트 단위로 얻기 위한 변수를 지정한다.
LPOVERLAPPED lpOverlapped //일단은 NULL
)

hmailSlot = CreateFile(
SLOT_NAME, //메일슬롯의 이름
GENERIC_WRITE, //사용되는 용도
FILE_SHARE_READ, 
NULL,
OPEN_EXISTING, // 생성 방식
FILE_ATTRIBUE_NORMAL,
NULL
);

CreateFile 함수의 첫 번째 전달인자는 생성 및 개방하고자 하는 파일 이름을 지정하는 용도
여기서는 데이터를 전달할 메일슬롯을 지정하는 용도로 사용

두 번째 전달인자는 파일의 개방모드를 지정(Read Or Write or R/W) 지금은 Sender이므로 쓰기모드에 해당이되는 GENERIC_WRITE를 인자로 전달

다섯 번째 전달인자는 파일의 생성방식을 결정짓는 용도로 사용됨(새로운파일생성 혹은 기존파일 접근)
이 예제에서는 Recciver가 이미 만들어 놓은 메일슬롯에 접근하는 것이 목적이기 때문에 OPEN_EXISTING을 전달한다. 이는 기존에 만들어진 파일을 개방할 때 사용하는 전달인자다.

BOOL WriteFIle(
HANDLE hFile // 데이터를 읽어 들일 파일을 지정한다. 여기서는 데이터를 읽어 들일 메일슬롯을 지정
LPCVOID lpBuffer // 전송할 데이터가 저장되어 있는 버퍼를 지정
DWORD nNumberOfBytesToWrite // 전송할 데이터 크기를 지정
LPDWORD lpNumberOfBytesWritten // 함수 호출 완료후 전송된 실제 데이터의 크기를 바이트 단위로 얻기 위한 변수의 주소를 지정한다.
LPOVERLAPPED lpOverlapped // 일단은 NULL

  • 메일슬롯의 고찰

메일슬롯과 IPC에 대한 고찰

메일슬롯은 한쪽 방향으로만 메시지를 전달할 수 있다(파이프중에서 Name 파이프는 기본적으로 양방향 데이터 송수신을 지원한다.)

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

메일슬롯은 생성과 동시에 Usage Count가 1이다. 참조하는 프로세스는 메일슬롯을 생성한 프로세스 하나이기 때문이다. 프로세스와 쓰레드를 제외한 다른 모든 커널 오브젝트는 생성과 동시에 Usage Count가 1이된다.

Signaled vs Non_Signaled

  • 커널 오브젝트의 두 가지 상태

Windows 운영체제에 의해서 생성되는 커널 오브젝트는 두가지 상태를 지님 이는 리소스에 특정 상황이 발생되었음을 알리기 위한 용도로 사용됨

  • 상태에 대한 이해

커널 오브젝트느는 Signaled 상태 (신호를 받은 상태), Non_Signaled(신호를 받지 않은 상태) 두가지의 상태를 가짐.

커널 오브젝트의 상태 정보는 멤버변수에 저장이 됨(Non_Signaled 상태라면 FALSE)

  • 프로세스 커널 오브젝트의 상태에 대한 이해

커널 오브젝트의 상태는 리소스에 특정 상황이 발생하였음을 알려주기 위해서 존재 -> 특성 상황이라는것이 리소스마다 다름 -> 커널 오브젝트의 상태가 변하는 시점은 커널 오브젝트의 종류에 따라서 달라짐 -> 여기서는 프로세스 커널 오브젝트에 대한 상태 변화에 대해서만 언급

프로세스가 생성될때 커널 오브젝트가 만들어지고 이때 Non-Signaled 상태에 놓이게됨 그러다가 프로세스가 종료되면 Signaled 상태로 변경된다 이런한 약속 덕분에 Signaled 상태의 프로세스 커널 오브젝트를 보고 프로세스가 종료되었음을 알수있음

종료된 프로세스는 다시 실행을 재개하지못하기 때문에 프로세스 커널 오브젝트의 상태는 일단 Signaled가 되면 절대로 다시 Non-Siganeld 상태로 변경되지 않는다.

  • 커널 오브젝트의 두 가지 상태를 확인하는 용도의 함수

커널 오브젝트의 상태 정보를 확인하는 데 사용되는 대표적인 함수

DWORD WaitForSingleObject(
HANDLE hHandle // 상태 확인을 원하는 커널 오브젝트의 핸들을 인자로 전달한다
DWORD dwMilliseconds // 이 함수는 인자로 전달된 hHandle이 가리키는 커널 오브젝트가 Signaled 상태가 되었을 때 반환한다. 즉 커널 오브젝트가 Signaled 상태가 될 때까지 기다리는 함수이다. 이 인자는 Signaled 상태가 될 때까지 기다릴 수 있는 최대 시간을 밀리세컨드 단위로 지정하는 용도로 사용되는 인자다. 상수 INFINITE를 인자로 전달하면, 커널 오브젝트가 Signaled 상태가 될 때까지 반환하지 않고 무한정 기다리게 된다.

WaitSingleObject 함수가 반환하는 상황은 다양하기 때문에 함수 호출이 완료된 후에 반환값을 확인해야만함

WAIT_OBJECT_0 : 커널 오브젝트가 Signaled 상태가 되었을 때 반환되는 값
WAIT_TIMEOUT : 커널 오브젝트가 Signaeld 상태가 되지 않고, dwMilliseconds 인자를 통해서 설정된 시간이 다 된 경우에 반환 되는 값
WAIT_ABANDONED: 소유 관계와 관련하여 함수가 정상적이지 못한 오류 발생에 의해서 반환하는 경우에 반환되는 값이다

만약에 상태를 확인하고자 하는 커널 오브젝트가 둘 이상이고 이들의 핸들이 한 밴열로 묶여있을때 활용 가능한 함수

DWORD WaitForMultipleObjects(
DWORD nCount // 배열에 저장되어 있는 핸들 개수를 전달
const HANDLE* lpHandles // 핸들을 저장하고 있는 배열의 주소 정보를 전달한다. 이 주소값을 시작으로 총 nCount개의 핸들이 관찰 대상이 된다.
BOOL bWaitAll // 관찰 대상이 모두 Signaled 상태가 되기를 기다리고자 하는지(TRUE 전달시), 아니면 하나라도 Signaled 상태가 되면 반환할 것인지(FALSE 전달 시)를 결정짓는다.
DWORD dwMilliseconds // WaitForSingleObject와 같은 의미를 지닌다. 타임아웃을 설정하는 용도로 사용한다.

  • 커널 오브젝트에 존재하는 종료코드(Exit Code)

프로세스가 종료되면서 전달하는 값을 가리켜 종료코드라 하고,이 종료코드는 종료되는 프로세스의 커널 오브젝트에 저장이됨. 이 종료코드를 활용해서 부모 프로세스에게 데이터를 전달할 수 있다.

WaitForSingleObejct(pi,INFINITE)를 사용하지않을경우 프로세스가 아직 진행중이라 GetExitCodeProcess함수 호출시 STILL_ACTIVE를 반환해 원하는 반환값을 얻지 못할수있음

이것만은 알고 갑시다

1.프로세스간 통신 기법이 별도로 존재하는 이유

프로세스는 개별적으로 독립적인 메모리 공간을 유지한다. 따라서 둘 이상의 프로세스가 데이터를 주고 받기 위해서는 Windows 운영체제에서 제공하는 기능상의 도움을 받아야만 한다.

2.메일슬롯의 특성

대표적인 프로세스 통신기법을 메일슬롯 기법이 있다. 이 기법은 단 방향 통신과 브로드 캐스팅 방식을 지원하는 통신기법이다.

3.커널 오브젝트의 두 가지 상태가 지니는 의미

커널 오브젝트는 Siganled 상태와,Non-Siganeld 상태 둘 중 하나의 상태에 놓이게 된다. 그리고 이 상태는 나머지 다른 하나의 상태로 변경되는데, 상태가 변경되는 시점은 커널 오브젝트의 종류에 따라서 다르다.

4.WaitForSingleObject,WaitMultipleObjects 함수와 커널 오브젝트의 관계

WaitForSingleObject 함수는 커널 오브젝트를 감시하는 역할을 한다. 커널 오브젝트가 Non-Signaled 상태에 있는 때에는 함수를 반환하지 않고 블로킹 상태에 있다가 함수가 Signaled 상태가 되면 함수를 빠져나오는 특성을 지닌다.

5 종료코드(Exit Code)

Main 함수의 return문에 의한 반환값은 프로세스의 종료코드로 커널 오브젝트에 저장된다. 이 종료코드를 얻기 위해서는 GetExitCodeProcess 함수를 호출하면 되고, 이 종료코드는 프로세스의 종료 이유를 담는 용도로 사용하는 것이 일반적이다.

0개의 댓글