프로세스간 통신(IPC)2
핸들테이블의 이해
- 위 상황에 대해 알아보자
- 프로세스가 메일 슬롯을 생성한다. 그리고 메일 슬롯 커널 오브젝트가 생성이 된다.
- 그러면 메일 슬롯에 접근하기 위한 핸들값과 커널 오브젝트의 주소 정보가 핸들테이블에 등록된다.
- 중요한점은 핸들 테이블은 프로세스별로 독립적이고 각 핸들 테이블은 자신들의 프로세스에게만 의미가 있다.
핸들 테이블의 상속
- 핸들 테이블은 부모 프로세스에서 조건에 맞는다면 자식 프로세스로 상속이 된다.
- 단 모든 핸들 정보를 상속하는게 아니라 핸들의 상속여부가 Y로 설정 되어 있는것들만 상속이 가능하다.
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
자식 프로세스에게 상속을 하기 위한 조건이 무엇인가?
- bInheritHandles
- 이 값이 핸들 테이블의 상속 여부를 결정한다.
- true이면 상속이 되고 false면은 상속이 되지 않는다.
핸들의 상속
어떠한 경우에 상속여부가 Y인가?
security attributes 구조체는 보안관리자이다. 이거를 설정하여 핸들 상속여부를 Y로 설정할 수 있다.
bInheritHandle을 TRUE로 설정하면 핸들의 상속여부가 Y가 된다.
핸들의 상속과 UC
현재 부모 프로세스만 접근이 가능해서 UC는 1인 상황이다.
- 근데 이제 자식 프로세스에게 상속을 했다고 가정하자
- 그러면 상속여부가 Y인 핸들은 상속이 되었다.
- 그런 경우 자식 프로세스의 핸들 테이블에 등록이 되어있기에 자식 프로세스도 접근이 가능하여 UC 값이 2가 된다.
위 이미지를 통해 핸들 테이블의 핸들이 자식 프로세스에게 정말 상속이 되었는지 확인해보자
- Receiver 프로세스는 메일 슬롯을 만들었다.
- sender 프로세스는 파일 생성하는 함수를 통해 메일 슬롯과의 연결고리를 생성한다.
- 이러한 연결고리도 커널에 의해서 생성되는 커널 오브젝트의 생성을 동반하는 리소스다.
- 메일 슬롯의 커널 오브젝트에는 sender 프로세스만 접근 가능한 상황이여서 UC = 1이다.
- sender 프로세스는 createProcess() 함수를 호출해서 자식 프로세스를 생성한다.
- 메일 슬롯의 커널 오브젝트에 접근 가능한 핸들의 상속 여부가 Y여서 자식 프로세스에게 상속이 되므로 자식 프로세스 역시 메일 슬롯의 커널 오브젝트에 접근이 가능해 UC=2가 된다.
Pseudo 핸들과 핸들의 중복1
seudo 핸들이라는건 가짜 핸들이라는 의미다. 핸들이라고 얘기하기 위한 기본적인 조건은 핸들 테이블에 등록이 되는거다. 등록되지 않으면 모두 가짜 핸들이다. 현재 실행 중에 있는 프로세스는 자신의 핸들을 얻는 방법으로 GetCurrentProcess 함수가 있다. 실제로 이 함수 호출을 통해 얻은 핸들을 이용해서 프로세스 자신의 커널 오브젝트에 접근이 가능하다. 그런데 이 함수 호출을 통핼 얻은 핸들을 가리켜 가짜 핸들 이라고 한다. 왜냐하면 이렇게 얻어진 핸들은 핸들 테이블에 등록되어 있지 않은 핸들이고, 다만 현재 실행 중인 프로세스를 참조하기 위한 용도로 정의 해 놓은, 약속된 상수가 반환되는 것이기 때문이다. 그런 가짜 핸들을 진짜 핸들로 바꿔야 하는 경우가 있다. 어떠한 경우냐면 아래와 같은 경우다. A 프로세스가 B 프로세스를 생성했는데 A 프로세스는 자기 자신의 핸들을 B에게 전달하고 싶다. 그러면 상속을 하면 되는데 실제 상속을 해보면 안된다. 왜냐면 자기 자신의 핸들 정보가 핸들 테이블에 없기 때문이다.
- DuplicateHandle(A 핸들, 256, B 핸들, &val)
- A 프로세스의 핸들 테이블에 등록된 256이라는 핸들을 B 프로세스의 핸들 테이블에 복사 해라는 함수다.
- 256이라는 핸들값이 B에 등록될 수 있고 다른 핸들값이 등록 될 수 있다.
- 256 핸들값은 A 프로세스에게 의미가 있는 값이다. B 프로세스에게는 의미가 없다.
- 핸들값은 프로세스 고유의 값임을 주의하자.
Pseudo 핸들과 핸들의 중복2
- 위 상황은 핸들값을 자기 자신에게 복사하는 상황이다.
- 주소값은 복사가 되지만 핸들값은 고유여서 다른 핸들값이 등록이 된다.
- UC 값은 2가 된다.
Pseudo 핸들과 핸들의 중복3
- GetCurrentProcess() 함수를 호출하면 이 함수를 호출한 프로세스 핸들값이 반환이 되는데 그 핸들값은 가짜 핸들값이다. 하지만 자기 자신을 가리킬 수 있는 핸들값이다.
- GetCurrentProcess() 를 3번 호출한 의미는 가짜 핸들 값을 나 자신의 핸들 테이블에 복사하라는 의미다. 그러면 가짜 핸들값이 진짜 핸들값이 된다.
- 즉 프로세스가 자기 자신의 핸들값을 자기 자신의 핸들 테이블에 등록이 된다.
정리
- GetCurrentProcess()를 실행하면 자기자신을 나타내는 '-1'값을 반환함.
- 기본적으로 자기자신의 핸들 값은 핸들태이블에 저장되어 있지 않기 때문에 자식 프로세스에게 바로 줄 수 있는 것은 오직 '-1'이라는 값 뿐임.
- 설령 자식 프로세스 B가 '-1'을 부모 프로세스A로 부터 받았다고 해도, B에서 '-1'을 사용하게 되면 A가 아닌 자기 자신 B를 가르키게 되어있음.
- 그렇기 때문에 A프로세스는 자신의 핸들 값을 먼저 복사해서 핸들테이블에 넣어 놔야함.
- DuplicateHandle(원본 핸들테이블,원본 핸들,저장될 핸들테이블, 저장될 핸들 )순서.
- 원본 핸들테이블에 자기자신을 뜻하는 '-1'값이 들어가니까, 자기자신의 핸들테이블을 의미하게 됨.
- 원본 핸들 값에 자기자신을 뜻하는 '-1'값이 들어가니까, 자기자신의 핸들을 의미하게 됨.
- 저장될 목적지 핸들테이블의 값에 자기자신을 뜻하는 '-1'값이 들어가니까, 자기자신의 핸들 테이블을 의미하게 됨.
- DuplicateHandle(GetCurrent(),GetCurrent(),GetCurrent(),&val)은 DuplicateHandle(-1,-1,-1,&val)이 되어서 DuplicateHandle(본인핸들테이블에서, 본인핸들을,본인핸들테이블의,&val로)복사해라가 됨.
파이프 방식의 IPC
IPC 별 특성
- 통신 범위는 동일한 컴퓨터상에서만 통신이 가능하냐 네트워크상으로 연결된 서로 다른 컴퓨터간에도 통신이 가능하냐를 물어보는거다.
- 메일 슬롯은 주소를 기반으로 전송이 이루어져서 서로 다른 컴퓨터에서 존재하는 프로세스간 통신도 가능하다.
- 이름있는 파이프
- 이름이 있다는건 주소가 있다는 의미다. 여기서의 주소는 네트워크상의 연결되어 있는 PC들을 구분할 수 있는 주소이다.
- 데이터를 줄 수도 있고 받을 수도 있는 양방향이다.
- 이름없는 파이프
- 이름이 없다는건 네트워크 상에 연결되어 있는 다른 컴퓨터와는 통신할 수 없음을 의미한다.
이름없는 파이프 예제
- createPipe() 함수를 호출하면 내부적으로 파이프가 생성이 된다.
- 첫번째 전달 인자를 통해서 read에 해당하는 끝(출구)을 얻을 수 있다.
- 두번째 전달 인자를 통해서 write에 해당하는 끝(입구)을 얻을 수 있다.
- createPipe() 함수를 통해 파이프의 입구와 출구에 접근할 수 있는 핸들값을 얻을 수 있다.
- 그러한 핸들값을 통해 데이터를 쓰고 읽는다 라는 특성을 알기
- 이름없는 파이프의 특성
- 자식 프로세스를 생성 했을때 파이프에 대한 출구, 입구 핸들값을 상속을 할 수 있다.
- 그러면 자식 프로세스도 파이프에 대해 접근이 가능하므로 부모 프로세스와 자식 프로세스간에 통신이 가능한 특성이 있다.
이름있는 파이프 프로그래밍 모델
실행 흐름을 살펴보자.
- server는 CreateNamedPipe()함수를 호출해서 파이프를 생성한다.
- CreateNamedPipe() 함수를 호출한건 예시를 들어 비유하면 집안에서 파이프를 가지고 있기만 한거다. 파이프를 통해 통신을 하고 싶으면 이 파이프를 외부로 노출을 시켜야 한다.
- server는 ConnectNamedPipe() 함수를 통해 파이프를 외부로 노출시킨다.
- 파이프를 누구나 연결 할 수 있도록 오픈한 상태이다. 즉 연결 대기 상태이다.
- 이제 외부에 있는 프로세스가 server와 통신을 하기 위해서는 우선 파이프와 연결을 해야한다.
- client는 파이프에 연결 요청을 한다.
- client는 CreateFile() 함수를 호출해서 파이프에 연결을 시도한다.
프로세스 환경 변수
- 환경 변수라는게 있는데 그냥 데이터 블럭이다.
- 이걸 부모 프로세스가 자식 프로세스에게 상속을 할 수 있다. 어떻게 보면 이거는 통신으로 볼 수 있다.
- 하지만 이런 방식으로 쓰기 보다는 환경 변수는 프로세스가 자신만의 고유 정보를 담앗다 뺏다 하는 용도로 사용된다.
- 환경 변수를 이용한 통신은 부가적인 기능이다.
- 핵심 기능은 프로세스마다 자신만의 고유한 정보를 넣었다 뺏다 하기 위해 사용된다.
환경변수라는거는 메모리 블럭에 할당된다. 환경변수에 대한 정보를 저장하기 위한 테이블이 존재한다고 보면 된다.
프로세스별로 독립적인 메모리 공간이 존재한다. 이 말은 조금 어색하다 왜냐면 프로세스라는거 자체가 원래 독립된 메모리 공간이기 때문이다. 프로세스 별로 환경변수 정보를 저장하기 위한 독립적인 메모리 블록이 존재한다.
부모 프로세스는 환경변수 테이블을 복사해서 자식 프로세스에게 넘겨줄 수 있다. 즉 통신을 한거다 라고 볼 수 있다.
'운영체제 > 뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
뇌를 자극하는 윈도우즈 시스템 프로그래밍 10장 (0) | 2024.09.07 |
---|---|
뇌를 자극하는 윈도우즈 시스템 프로그래밍 9장 (0) | 2024.09.07 |
뇌를 자극하는 윈도우즈 시스템 프로그래밍 7장 (0) | 2024.09.05 |
뇌를 자극하는 윈도우즈 시스템 프로그래밍 6장 (0) | 2024.09.05 |
뇌를 자극하는 윈도우즈 시스템 프로그래밍 5장 (0) | 2024.05.04 |