IT공부/IT서적

[뇌를 자극하는 윈도우즈 시스템 프로그래밍] 8장. 프로세스간 통신(IPC) - 2

shine94 2025. 1. 21. 22:19

* 해당 글은 윤성우의 뇌를 자극하는 윈도우즈 시스템 프로그래밍 도서를 읽고 정리한 글입니다

   https://product.kyobobook.co.kr/detail/S000001223395

 

뇌를 자극하는 윈도우즈 시스템 프로그래밍 | 윤성우 - 교보문고

뇌를 자극하는 윈도우즈 시스템 프로그래밍 |

product.kyobobook.co.kr

 

 

 

 

* 핸들 테이블

   핸들 정보를 저장하고 있는 테이블로서 프로세스별로 독립적이다

 

* Usage Count

   커널 오브젝트를 참조하는 프로세스 개수만큼 커널 오브젝트의 Usage Count 증가

 

* 프로세스가 핸들을 얻게 되었다는 의미

   핸들 테이블에 해당 핸들에 대한 정보가 갱신(추가)되었음을 의미하는 것이다

 

* GetCurrentProcess 함수

   현재 실행중인 프로세스 자신의 핸들을 얻을 수 있다

   [그런데] 이 함수를 통해 얻은 핸들을 가리켜 가짜 핸들(Pseudo 핸들)이라고 함

                 현재 실행중인 프로세스를 참조하기 위한 용도로 정의해 놓은, 약속된 상수가 반환되는 것이기 때문임

   [따라서] 자식 프로세스에 상속되지 않는다

                 현재 실행중인 프로세스에서 진짜 핸들(Real Handle)을 얻기 위해서는 DuplicateHandle 함수를 이용하면 됨 

 

* DuplicateHandle 함수

   핸들 복사, 진짜 핸들(Real Handle)

BOOL DuplicateHandle(
  [in]  HANDLE   hSourceProcessHandle,
  [in]  HANDLE   hSourceHandle,
  [in]  HANDLE   hTargetProcessHandle,
  [out] LPHANDLE lpTargetHandle,
  [in]  DWORD    dwDesiredAccess,
  [in]  BOOL     bInheritHandle,
  [in]  DWORD    dwOptions
);

 

hSourceProcessHandle

 : 복제할 핸들을 소유하고 있는 프로세스에 대한 핸들
hSourceHandle

 : 복제할 핸들을 지정
hTargetProcessHandle

 : 복제할 핸들을 소유할 프로세스를 지정
lpTargetHandle

 : 복제될 핸들값을 저장할 변수의 주소를 지정
dwDesiredAccess

 : 복제된 핸들의 접근 권한
bInheritHandle

 : 복제된 핸들의 상속 여부 지정

   TRUE 전달 시 새로운 자식 프로세스로 상속되고, FALSE 전달 시 상속되지 않는다
dwOptions

 : 비트 단위 OR 연산자를 통해 동시 전달 가능

   없음과 아래의 옵션 선택 가능

   - DUPLICATE_CLOSE_SOURCE

   - DUPLICATE_SAME_ACCESS

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/handleapi/nf-handleapi-duplicatehandle

 

DuplicateHandle 함수(handleapi.h) - Win32 apps

개체 핸들을 복제합니다.

learn.microsoft.com

 

* 메일슬롯

   두 프로세스가 서로 메시지를 주고 받을 수 있기 위해서는 두 개의 메일슬롯을 생성해야 하는 번거로움이 있다

   이러한 번거로움을 덜기 위해 한 개만 생성하여 메시지를 주고 받고자 할 때, 사용 가능한 IPC 기법이 있는데 

   이름 없는 파이프(Anonymous Pipe), 이름 있는 파이프(Named Pipe)이다

   ㄴ 메일슬롯

        단방향 통신(서로 메시지를 주고 받기 위해 두 개를 생성해야 한다)

        메일슬롯에 할당된 주소를 기반으로 통신하기 때문에 관계없는 프로세스들 사이에서도 통신 가능

   ㄴ 이름없는 파이프

        단방향 통신(서로 메시지를 주고 받기 위해 두 개를 생성할 필요 없다)

        파이프를 통해서 생성된 핸들을 기반으로 통신하기 때문에 프로세스들 사이에서는 관계가 있어야 함

   ㄴ 이름있는 파이프

         양방향 통신

         이름을 기반으로 통신하기 때문에 관계없는 프로세스들 사이에서도 통신이 가능하다

 

* CreatePipe 함수

   이름없는 파이프 생성

   한쪽 끝에서는           데이터 들어가고

   다른 한쪽 끝에서는   들어간 데이터가 흘러나오는 것이 파이프의 원리인 것처럼

   두 개의 끝을 가지고 있어야 한다

BOOL CreatePipe(
  [out]          PHANDLE               hReadPipe,
  [out]          PHANDLE               hWritePipe,
  [in, optional] LPSECURITY_ATTRIBUTES lpPipeAttributes,
  [in]           DWORD                 nSize
);

 

hReadPipe

 : 데이터를 읽기 위한 파이프의 끝

hWritePipe

 : 데이터를 쓰기 위한 파이프의 끝
lpPipeAttributes

 : 보안 관련 정보

nSize

 : 파이프의 버퍼 사이즈를 지정하는 용도

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe

 

CreatePipe 함수(namedpipeapi.h) - Win32 apps

익명 파이프를 만들고 파이프의 읽기 및 쓰기 끝에 핸들을 반환합니다.

learn.microsoft.com

 

* CreateNamedPipe 함수

   이름있는 파이프 생성

   ConnectNamedPipe 함수 호출하여 파이프 연결

   Server-Client 구조

HANDLE CreateNamedPipeA(
  [in]           LPCSTR                lpName,
  [in]           DWORD                 dwOpenMode,
  [in]           DWORD                 dwPipeMode,
  [in]           DWORD                 nMaxInstances,
  [in]           DWORD                 nOutBufferSize,
  [in]           DWORD                 nInBufferSize,
  [in]           DWORD                 nDefaultTimeOut,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

 

lpName

 : 파이프 이름을 지정
dwOpenMode

 : 파일을 개방할 때 모드를 지정
dwPipeMode

 : 파이프 모드 지정
nMaxInstances

 : 생성할 수 있는 파이프의 최대 개수 지정
nOutBufferSize

 : 이름있는 파이프의 출력 버퍼 사이즈를 지정
nInBufferSize

 : 이름있는 파이프의 입력 버퍼 사이즈를 지정
nDefaultTimeOut

 : WaitNamedPipe 함수에 적용할 기본 만료 시간을 밀리세컨드 단위로 지정
lpSecurityAttributes

 : 보안 속성을 지정

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/winbase/nf-winbase-createnamedpipea

 

CreateNamedPipeA 함수(winbase.h) - Win32 apps

ANSI(CreateNamedPipeA) 함수(winbase.h)는 명명된 파이프의 인스턴스를 만들고 후속 파이프 작업에 대한 핸들을 반환합니다.

learn.microsoft.com

 

* ConnectNamedPipe 함수

   이름있는 파이프 서버 프로세스가 클라이언트 프로세스가 연결되기를 기다리도록 한다

   즉, 이름있는 파이프 서버의 대기 상태

BOOL ConnectNamedPipe(
  [in]                HANDLE       hNamedPipe,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe

 

ConnectNamedPipe 함수(namedpipeapi.h) - Win32 apps

명명된 파이프 서버 프로세스가 클라이언트 프로세스가 명명된 파이프의 인스턴스에 연결되기를 기다릴 수 있도록 합니다.

learn.microsoft.com

 

hNamedPipe

 : 파이프의 핸들을 전달
lpOverlapped
 : 중첩 I/O를 위한 전달인자

 

BOOL isSuccess = ReadFile(
	hPipe,				// 서버 파이프와 연결된 핸들
	readDataBuf,			// 데이터 수신할 버퍼
	BUF_SIZE * sizeof(TCHAR),	// 버퍼 사이즈
	&bytesRead,			// 수신한 바이트 수
	NULL);

 

ReadFile는 읽어온 데이터의 크기를 바이트 단위로 반환한다
    즉, bytesRead는 문자 수가 아닌 바이트 수를 나타낸다
TCHAR는 유니코드 설정 여부에 따라 크기가 다르다
     ANSI 환경 : 1바이트 (sizeof(TCHAR) == 1)
     유니코드 환경 : 2바이트 (sizeof(TCHAR) == 2)
문자열 끝에 올바른 널 종료(\0)를 추가하기 위해서는 문자 단위 인덱스를 계산해야 한다
    문자 단위 인덱스를 계산하려면, bytesRead를 sizeof(TCHAR)로 나눠야 한다

 

▶ 왜 나눠야 하는가?

    결국, 문자 크기(TCHAR의 크기)가 달라지면 배열 인덱스의 위치도 달라지기 때문

    1바이트: c[0]과 c[1] 사이의 간격 = 1칸

    2바이트: c[0]과 c[1] 사이의 간격 = 2칸

 

readDataBuf[bytesRead / sizeof(TCHAR)] = 0;	// 문자 단위로 계산하여 널 종료 추가

 

// 파일 크기 계산
fseek(filePtr, 0, SEEK_END);
fileSize = ftell(filePtr);
rewind(filePtr);	// 파일 포인터를 다시 시작 위치로 이동

 

* WaitNamedPipe 함수

   내가 설정한 time-out이나 디폴트로 정의된 시간(CreateNamedPipe 함수의 7번째 인자를 통해 전달)만큼 

   이름있는 파이프 서버에 연결할 수 있을 때까지 기다린다

BOOL WaitNamedPipeA(
  [in] LPCSTR lpNamedPipeName,
  [in] DWORD  nTimeOut
);


lpNamedPipeName

 : 파이프 이름
nTimeOut

 : 타임-아웃 시간 설정

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/winbase/nf-winbase-waitnamedpipea

 

WaitNamedPipeA 함수(winbase.h) - Win32 apps

WAITNamedPipeA(ANSI) 함수(winbase.h)는 시간 제한 간격이 경과하거나 지정된 명명된 파이프의 instance 연결에 사용할 수 있을 때까지 대기합니다(즉, 파이프의 서버 프로세스에 파이프에 보류 중인 Connect

learn.microsoft.com

 

SetNamedPipeHandleState 함수

   파이프 속성 변경

BOOL SetNamedPipeHandleState(
  [in]           HANDLE  hNamedPipe,
  [in, optional] LPDWORD lpMode,
  [in, optional] LPDWORD lpMaxCollectionCount,
  [in, optional] LPDWORD lpCollectDataTimeout
);

 

hNamedPipe

 : 파이프와의 연결 속성을 변경시키기 위한 핸들 지정
lpMode

 : 읽기 모드와 리턴방식에 대한 값을 비트 OR 연산으로 전달
lpMaxCollectionCount

 : 서버로 데이터를 보내기에 앞서서 버퍼링할 수 있는 최대 바이트 크기를 지정하는데 사용
lpCollectDataTimeout

 : 서버로 데이터를 보내기 앞서서 버퍼링을 허용하는 최대 시간을 지정하는데 사용

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate

 

SetNamedPipeHandleState 함수(namedpipeapi.h) - Win32 apps

지정된 명명된 파이프의 읽기 모드 및 차단 모드를 설정합니다. 지정된 핸들이 명명된 파이프의 클라이언트 끝에 있고 명명된 파이프 서버 프로세스가 원격 컴퓨터에 있는 경우 함수를 사용하

learn.microsoft.com

 

* 프로세스 환경변수

   자식 프로세스에게 핸들 정보를 전달하기 위해서 파일을 이용했으나 이보다 더 안정적인 방법이 있다

   바로 프로세스 환경 변수를 이용하는 것이다

 

* SetEnvironmentVariable 함수

   현재 프로세스에 대해 환경변수를 설정

BOOL SetEnvironmentVariable(
  [in]           LPCTSTR lpName,
  [in, optional] LPCTSTR lpValue
);

 

lpName

 : key에 해당하는 값 지정
lpValue

 : value에 해당하는 값 지정

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/winbase/nf-winbase-setenvironmentvariable

 

SetEnvironmentVariable 함수(winbase.h) - Win32 apps

SetEnvironmentVariable 함수(winbase.h)는 현재 프로세스에 대해 지정된 환경 변수의 내용을 설정합니다.

learn.microsoft.com

 

* GetEnvironmentVariable 함수

   호출 프로세스의 환병 블록에서 지정된 변수의 내용을 검색

DWORD GetEnvironmentVariable(
  [in, optional]  LPCTSTR lpName,
  [out, optional] LPTSTR  lpBuffer,
  [in]            DWORD   nSize
);

 

lpName,

 : key 값 전달
lpBuffer

 : value 값을 저장하기 위한 메모리의 주소를 지정
nSize
 : lpBuffer가 가리키는 메모리의 크기를 지정

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/winbase/nf-winbase-getenvironmentvariable

 

GetEnvironmentVariable 함수(winbase.h) - Win32 apps

GetEnvironmentVariable 함수(winbase.h)는 호출 프로세스의 환경 블록에서 지정된 변수의 내용을 검색합니다.

learn.microsoft.com

 

* CreateToolhelp32Snapshot 함수

   현재 실행중인 프로세스의  사진을 찍으라는 의미

   ㄴ 프로세스가 사용하는 힙, 모듈, 스레드도 가능

HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);


dwFlags

 : 스냅샷에 포함할 시스템의 부분, 이 매개변수는 값이 하나 이상일 수도 있음

th32ProcessID

 : 스냅샷에 포함할 프로세스의 프로세스 식별자, 이 매개변수는 현재 프로세스를 나타내는 0일 수 있음

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot

 

CreateToolhelp32Snapshot 함수(tlhelp32.h) - Win32 apps

지정된 프로세스의 스냅샷 이러한 프로세스에서 사용되는 힙, 모듈 및 스레드를 사용합니다.

learn.microsoft.com