HotFoxy
불여우의 전직 이야기
게임 서버 개발자가 되어 보죠!
전체 방문자
오늘
어제
  • 분류 전체보기 (135)
    • 연구한 이야기 (26)
      • 깊게 공부해보기 (7)
      • 문제 해결 이야기 (12)
      • 맡은 업무 이야기 (6)
    • 전직 이야기 (0)
      • 1년이라는 시간 (5)
      • 프로카데미 이야기 (5)
    • 공부한 이야기 (87)
      • 알고리즘 (7)
      • 리눅스 (11)
      • 클라우드 (24)
      • 윈도우 OS (17)
      • 윈도우 소켓 프로그래밍 (11)
      • 네트워크 (16)
      • Docker & K8S (0)
      • 기타 (1)
    • 자격증 이야기 (12)
  • MSB : Mad Square's Brawl
  • GITHUB

인기 글

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
HotFoxy
공부한 이야기/윈도우 OS

VIII : 프로세스간 통신 (IPC) 2

공부한 이야기/윈도우 OS

VIII : 프로세스간 통신 (IPC) 2

2023. 4. 29. 21:54

VIII : 프로세스간 통신 (IPC) 2

뇌를 자극하는 윈도우즈 시스템 프로그래밍 을 읽고 정리한 문서입니다 ;)

핸들 테이블과 오브젝트 핸들의 상속

유능한 윈도우 프로그래머는 프로세스 핸들 테이블이 어떻게 관리되는지를 이해하고 있어야 한다는 Jeffrey Richter의 말이 있듯이, 핸들 테이블에 대한 이해는 IPC 를 알기 위한 필수 요소이다.

핸들은 그저 단순한 정수값인데, 이를 가지고 커널 모드에 있는 커널 오브젝트에 접근할 수 있는 이유는 무엇일까? 이는 프로세스 핸들 테이블이 있기 때문이다.

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

CreateProcess() 함수를 호출하면 새로운 자식 프로세스가 생성된다. 이 때 BOOL bInheritHandles 라는 이름을 가진 다섯 번째 인자가 무엇이냐에 따라 부모 프로세스 핸들 테이블에 등록되어 있는 핸들 정보는 새롭게 생성되는 자식 프로세스에게 상속될 수 있다.

실제 핸들 테이블에는 핸들마다 상속 여부를 결정짓기 위한 컬럼이 존재하는데, 이는 자식에게 만일 부모의 핸들 테이블이 상속된다 하더라도 상속가능 플래그는 그대로 유지된다.

프로세스가 핸들을 얻게 되었다는 의미는 프로세스 핸들 테이블에 해당 핸들에 대한 정보가 갱신되었음을 의미하는 것이다.

예를 들어 CreateMailslot() 함수를 통해 메일슬롯을 생성했다면, 메일슬롯 리소스 생성 → 커널 오브젝트 생성 → 핸들 정보가 프로세스 핸들 테이블에 갱신 → 해당 함수가 핸들값 반환 이라는 네 단계의 절차를 거치는 것이다.

핸들의 상속가능 여부는 리소스가 생성되는 순간에 프로그래머에 의해 결정된다.

핸들을 리턴하는 대부분의 리소스 생성 API에는 파라미터로 LPSECURITY_ATTRIBUTES 라는 값을 전달하게 되어 있는데, 이 구조체의 BOOL bInheritHandle 값을 어떻게 설정하여 넣느냐에 따라서 해당 핸들이 상속될 수 있음이 정해진다.

따라서, CreateProcess 시에는 자식 프로세스에게 부모의 핸들 테이블을 상속시킬 것인지를 결정해야 하고, 또한, 이렇게 만들어진 핸들을 상속 가능으로 할것인지를 설정해야 하는 두 가지 사항을 결정해야 하는 것이다.

Pseudo 핸들과 핸들의 중복

실행 중에 있는 프로세스 자신의 핸들을 얻는 방법으로서 GetCurrentProcess() 함수를 쓸 수 있지만, 이 함수 호출을 통해 얻는 핸들은 가짜 핸들이다. 이는 실제로 프로세스 핸들 테이블에 존재하는 것이 아닌, 자기 자신을 의미하도록 사전 정의된 특수한 값을 리턴하는 것이기 때문이다.

현재 실행 중인 프로세스가 가짜 핸들 이외에 핸들 테이블에 등록되어 있는 진짜 핸들을 얻어야 할 경우에는, DuplicateHandle()이라는 함수를 사용한다.

BOOL DuplicateHandle(
    HANDLE hSourceProcessHandle,
    HANDLE hSourceHandle,
    HANDLE hTargetProcessHandle,
    LPHANDLE lpTargetHandle,
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwOptions
);
  • HANDLE hSourceProcessHandle : 복제할 핸들을 소유하는 프로세스의 핸들이다.
  • HANDLE hSourceHandle : 복제할 핸들을 지정한다.
  • HANDLE hTargetProcessHandle : 복제된 핸들을 소유할 프로세스의 핸들이다.
  • LPHANDLE lpTargetHandle : 복제된 핸들이 저장될 변수의 주소이다.

이 핸들 복사 API를 이용하면, GetCurrentProcess()를 통해 얻은 가짜 핸들을 진짜 핸들로 얻어낼 수 있게 된다.

파이프 방식의 IPC

윈도우의 파이프 매커니즘에는 두 가지 종류가 있는데, 하나는 이름없는 파이프 (Anonymous Pipe)이고, 하나는 이름있는 파이프 (Named Pipe) 이다.

  • 메일슬롯
    • 브로드캐스트 방식의 단방향 통신방식을 취하며, 메일슬롯 이름이 주소를 의미하므로 서로 관계 없는 프로세스간 통신이 가능해진다.
  • 이름없는 파이프
    • 단방향 통신방식을 취하며, 파이프를 생성하여 얻은 핸들을 통해 통신하기 때문에 서로 관련있는 프로세스간 통신을 가능하게 해준다.
  • 이름있는 파이프
    • 양방향 통신방식을 취하며, 파이프 주소를 통하면 서로 관계 없는 프로세스간 통신이 가능해진다.

이름없는 파이프

이름없는 파이프는 데이터를 한 쪽 방향으로 흐르게 해주는 연결 통로이다.

BOOL CreatePipe(
    PHANDLE hReadPipe,
    PHANDLE hWritePipe,
    LPSECURITY_ATTRIBUTES lpPipeAttributes,
    DWORD nSize
);
  • PHANDLE hReadPipe : 파이프의 양쪽 끝단 중, 데이터를 읽기 위한 파이프 끝에 해당하는 핸들을 얻게 된다.
  • PHANDLE hWritePipe : 파이프의 양쪽 끝단 중 데이터를 쓰기 위한 파이프 끝에 해당하는 핸들을 얻게 된다.
  • DWORD nSize : 파이프의 버퍼 사이즈를 지정하는 용도이다.

이름있는 파이프

이름있는 파이프는 양방향 통신으로 서버-클라이언트의 구조를 가진다.

서버는 CreateNamedPipe 함수를 통해서 파이프를 생성하고, ConnectNamedPipe 함수를 호출하여 파이프를 연결 요청을 기다리는 파이프로 상태를 변경한다.

이후 클라이언트는 서버가 만들어놓은 파이프에 연결하기 위해 리소스를 생성하는데, 이는 CreateFile 함수가 모든 것을 처리하게 된다.

HANDLE CreateNamedPipe(
    LPCTSTR lpName,
    DWORD dwOpenMode,
    DWORD dwPipeMode,
    DWORD nMaxInstances,
    DWORD nOutBufferSize,
    DWORD nInBufferSize,
    DWORD nDefaultTimeOut,
    LPSECURITY_ATTRIBUTES lpSA
);
  • LPCTSTR lpName : 파이프 이름을 지정한다. \\.\pipe\pipename 같은 형태로 구성된다.
  • DWORD dwOpenMode : 파일의 읽기 쓰기 모드를 지정하는 것과 같은 이치이다.
    • PIPE_ACCESS_DUPLEX : 읽기 쓰기가 모두 가능하도록 설정
    • PIPE_ACCESS_INBOUND : 파이프 생성 함수 호출자 입장에서 읽기만 가능하다.
    • PIPE_ACCESS_OUTBOUND : 파이프 생성 함수 호출자 입장에서 쓰기만 가능하다.
  • DWORD dwPipeMode : 데이터 전송 타입, 데이터 수신 타입, 블로킹 모드를 설정할 수 있다.
    • 데이터 전송방식 : PIPE_TYPE_BYTE 혹은 PIPE_TYPE_MESSAGE 로서, 전자는 바이너리 형태의 전송, 후자는 텍스트 모드의 전송이다.
    • 데이터 수신방식 : PIPE_READMODE_BYTE 혹은 PIPE_READMODE_MESSAGE 로서, 전송 방식의 타입들과 의미가 같다.
    • 함수 리턴방식 : PIPE_WAIT 혹은 PIPE_NOWAIT 으로서, 반드시 PIPE_WAIT이 사용된다.
  • DWORD nMaxInstances : 생성할 수 있는 파이프의 최대 개수로서, 클라이언트 수용 능력을 의미한다.

위 함수를 통해 만든 파이프를 연결 요청 대기 상태로 변경시킬 때 사용하는 함수는 다음과 같다.

BOOL ConnectNamedPipe(
    HANDLE hNamedPipe,
    LPOVERLAPPED lpOverlapped
);

파이프의 최대 개수는 처음 CreateNamedPipe 함수가 호출될 때 지정되고, 그 이후부터는 단지 파이프 생성의 목적만으로 호출된다.

파이프로 데이터 전송 시에, FlushFileBuffers 함수를 통해 출력 버퍼를 비워, 즉시 전송시킬 수 있다. 또한, DisconnectNamedPipe 함수는 파이프 연결을 끊음으로서 클라이언트가 에러 메시지를 받을 수 있도록 도와준다.

서버의 파이프에 연결할 때는, WaitNamedPipe() 함수를 이용하며, 이 때도 두 번째 인자로 타임아웃을 설정할 수 있다.

마지막으로, 이름있는 파이프의 속성을 변경하기 위해서는 SetNamedPipeHandleState 를 사용한다.

프로세스 환경변수

부모 프로세스가 자식 프로세스에게 데이터를 전달하기 위해서 쓸 수 있는 방법 중 하나로서, 프로세스 환경변수를 사용하는 방법이 있다. 프로세스별로 별도의 메모리 공간에 문자열 데이터를 저장하고 관리할 수 있도록 되어있기 때문이다.

SetEnvironmentVariable() 함수와 GetEnvironmentVariable() 함수를 사용하면 이 값을 쓰고 읽을 수 있다.

만일 CreateProcess() 함수를 사용할 때, 환경 변수를 의미하는 일곱 번째 인자로 NULL을 전달하게 되면 부모 프로세스에 등록되어 있는 환경변수를 등록하게 된다.

이것만은 알고 갑시다

  • 핸들 테이블
    • 커널 오브젝트와 핸들 사이에 핸들 테이블이 존재한다. 덕분에 핸들의 정수값만 가지고도 커널 오브젝트의 식별이 가능하다.
  • 핸들과 핸들 테이블
    • 핸들 테이블은 프로세스별로 독립적이다. 그리고 숫자가 핸들로서 의미를 지니기 위해서는 해당 숫자가 핸들 테이블에 등록되어야 한다.
  • 핸들의 상속
    • 핸들은 자식 프로세스를 생성하는 과정에서 상속할 수 있다. 핸들이 자식 프로세스에게 상속된다는 말은 부모 프로세스의 핸들 테이블 정보가 자식 프로세스의 핸들 테이블에 복사된다는 것이다.
  • 가짜 핸들 (Pseudo 핸들)
    • GetCurrentProcess 함수를 통해 얻은 핸들은 가짜 핸들이다. 이는 핸들 테이블에 등록된 값이 아닌 이미 의미를 가지는 상수값이기 때문이다. 핸들 테이블의 핸들을 얻기 위해서는 DuplicateHandle 함수를 사용해야 한다.
  • 파이프
    • 이름없는 파이프와 이름있는 파이프로 나뉘며, 이름없는 파이프는 단방향 통신이라는 점과 관계가 있는 프로세스간의 통신이 가능하다는 특징이 있다. 이름있는 파이프는 양방향 통신이며 주소 체계를 통해 관계 없는 다수의 프로세스간에도 통신이 가능하다.
저작자표시 (새창열림)

'공부한 이야기 > 윈도우 OS' 카테고리의 다른 글

X : 컴퓨터 구조에 대한 세 번째 이야기  (0) 2023.04.29
IX : 스케줄링 알고리즘과 우선순위  (0) 2023.04.29
VII : 프로세스간 통신 (IPC) 1  (0) 2023.04.29
VI : 커널 오브젝트와 오브젝트 핸들  (0) 2023.04.29
V : 프로세스의 생성과 소멸  (0) 2023.04.29
  • 핸들 테이블과 오브젝트 핸들의 상속
  • Pseudo 핸들과 핸들의 중복
  • 파이프 방식의 IPC
  • 이름없는 파이프
  • 이름있는 파이프
  • 프로세스 환경변수
  • 이것만은 알고 갑시다
'공부한 이야기/윈도우 OS' 카테고리의 다른 글
  • X : 컴퓨터 구조에 대한 세 번째 이야기
  • IX : 스케줄링 알고리즘과 우선순위
  • VII : 프로세스간 통신 (IPC) 1
  • VI : 커널 오브젝트와 오브젝트 핸들
HotFoxy
HotFoxy
1년 동안의 고군분투 전직 이야기! ..가 완료되어, 게임개발자로 살아남는 이야기!

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.