핸들 테이블과 오브젝트 핸들의 상속
-핸들 테이블의 이해
핸들 테이블은 핸들 정보를 저장하고 있는 테이블로서 프로세스별로 독립적이다. 쉽게 말하면 각각의 프로세스가 자신만의 핸들 테이블을 하나씩 구성하고 관리한다.
단 모든핸들이 아닌 상속이 가능한 핸들만 상속이 가능하다.
상속한경우 핸들과 주소가 그대로 상속된다-
BOOL CreateProcess(
....
BOOL bInheriyHandles // 핸들 테이블 상속 여부
....
)
어떻게 상속 여부를 y로 두는가?
SECURITY_ATTRIBUTES sa; // 보안정보를 담당하는 구조체를 초기화
sa.nLength = sizeof(sa);
sa.lpSecurityDesciptor = null;
sa.bInheritHandle = true; // 핸들의 상속가능여부를 yes
...
CreateMailslot(....,&sa); &sa는 4번째 전달인자
UC가 1인 부모 프로세스의 핸들을 상속여부가 Y일때 상속을 하면
자식의 핸들테이블에 등록되었기때문에 UC가 1증가해 2가된다
부모프로세스의 메일슬롯과 연결을 위한 핸들이 있을때 이를 자식프로세스에게 상속해주면 자식프로세스도 이 핸들을통해 리시버와의 통신이 가능하다.
-Pseudo(가짜) gosemfrhk gosemfdml wndqhr
GetCurrentProcess로 얻은 해들은 핸들테이블에 등록되지않는 가짜 핸들이다.
하지만 가짜핸들을 진짜 핸들로 만들어야 한다면
A가 자식 프로세스 B에게 자기 자신의 핸들을 전달하고싶다면?
왜 상속 해줄려고 하는가? : 자식프로세스 입장에서 부모프로세스가 종료되길 기다릴수도 있음 이를 위해 핸들정보가 필요함
자기자신의 핸들테이블을 B에게 상속해준다.(하지만 자기자신의 핸들은 핸들테이블에 등록되지않는다.)
DuplicateHandle (A 핸들 // 프로세스의 핸들정보
, 256 //핸들의 번호
,B 핸들 //프로세스 핸들
, &val(364) //복사하고싶은 핸들의 번호
,...);
같은 핸들을 복사하면 UC가 2가됨 -> CloseHandle 2번해줘야함
가짜 핸들
가짜 핸들값
가짜 핸들이 가리키는값(자기자신)
원하는 번호
결국 가짜핸들 즉 자신을 가리키는 핸들이 자신의 핸들테이블에 등록이되고 자식에게 상속이 가능 해짐
파이프 방식의 IPC
메일슬롯은 서로 관련이 없는 프로세스들(네트워크로 연결되어 통신하는 프로세스들이나 부모 자식간의 연관 관계가 전혀 없는 프로세스들) 사이에서 통신할 때 유용한 IPC 기법이다.
-파이프
이름-> 주소가 있다 네트워크에 연결된 PC를 구별할수있는 주소
이름이없다->주소가없다->네트워크에 연결된 다른컴퓨터와 통신 불가능하다.
이름있는 파이프 : 한번연결이되면 양방향의 특성으로 데이터를 줄수도 받을수도있다.
-이름없는 파이프
CreatePipe(&hReadPipe,&hWritePipe,NULL,0)를 통해 파이프를 생성가능
입구와 출구에 해당하는 핸들을 반환해줌
자식프로세스를 생성했을때 핸들을 상속함으로써 부모자식간의 연결이 가능
1.hReadPipe : 파이프는 입구와 출구 두개의 끝을 가지고 있다. 따라서 파이프 생성 시 각각의 끝에 접근하기 위한 두 개의 핸들을 얻게 되는데, 이 인자를 통해서는 데이터를 일기 위한 파이프 끝에 해당하는 핸들을 얻게된다.
2.hWritePipe : 다른 한쪽 끝(데이터를 쓰기 위한)에 해당하는 핸들을 얻게된다.
3.lpPipeAttributes : 보안 관련 정보를 전달할 때 사용된다. 앞서 핸들의 상속 특성을 지정할 때 사용할 수 있음을 살펴 보았다.
4.nSize : 파이프의 버퍼 사이즈를 지정하는 용도로 사용. 전달되는 값으로 버퍼의 크기가 결정되는것은 아니고 참조로 활용되지만 값이 크면 큰버퍼가 작으면 작은 버퍼가 생성된다. 그리고 0을 인자로 전달하면 디폴트 사이즈로 버퍼 크기가 결정된다.
-이름있는 파이프 프로그래밍 모델
1.우선 서버쪽에서 파이프를 만들어야함(CreateNamedPipe)
ConnectNamedPipe 함수로 외부에 노출 연결 대기 상태로 전환
Client의 CreateFile 함수 호출에 의한 파이프 오픈(연결)
1.lpName : 파이프 이름을 지정한다. 파이프 이름은 다음과 같은 형태로 구성한다. \.\pipe\pipename
2.dwOpenMode : 파일을 개방할 때 읽/쓰 기 모드를 지정한다
3.dwPipeMode : 이 전달인자를 통해 데이터 전송 타입,데이터 수신 타입,블로킹 모드 이렇게 총 3가지를 설정한다.
4.nMaxInstance : 생성할 수 있는 파이프이 최대 개수를 지정한다. 여기서 지정하는 개수만큼 파이프 클라이언트의 연결 요청을 수용할 수있다 1~255의 범위를 가지며 무조건 생성되는것이 아닌 생성 가능하 최대 개수만큼 생성을 허용한다.
5.nOutBufferSize : 이름있는 파이프의 출력 버퍼 사이즈를 지정한다, 0 : Windows Default
6.nInBufferSize : 이름있는 파이프의 입력 버퍼 사이즈를 지정한다. 0 입력 시 Windows 디폴트 값이 설정된다.
7.nDefaultTimeOut : WaitNamedPipe함수에 적용할 기본 만료 시간을 밀리세컨드 단위로 지정
8.lpSecuritAttributes : 보안속성 지정
dwPipeMode에는 다음 설정값들이 OR 연산되어 전달됨
데이터 전송방식 : PIPE_TYPE_BYTE(바이트),PIPE_TYPE_MESSAGE(메시지)
데이터 전송 시 바이너리 형태로 전송할 것인지,메시지 방식인지 결정
파일 개방시 바이너리 OR 텍스트와 비슷
데이터 수신 : PIPE_READMODE_BYTE,PIPE_READMODE_MESSAGE
: 전송방식과 동일
함수 리턴방식 : PIPE_WAIT(블로킹),PIPE_NOWAIT(넌-블로킹)
: 무조건 PIPE_WAIT가 전달될다고 기억
파이프를 연결 요청 대기 상태로 변경시킬 때 사용하는 함수
BOOL ConnectNamedPipe (
HANDLE hNamedPipe // CreateNamedPipe 함수 호출을 통해서 생성한 파이프의 핸들을 전달한다.
LPOVERLAPPED lpOverlapped // 중첩 I/O를 위한 전달 인자이다
CreateNamedPipe의 함수의4번째 인자인최대 인스턴스 개수는 처음에 호출된 값만 의미가있음
최대 인스턴스 갯수는 파이프 이름마다 정해진다.
클라이언트 타임아웃 : 클라이언트가 연결을 요청할때 최대인스턴스개수만큼 연결되어있다면 이 인자의 시간만큼 클라이언트가 Default로 기다리게됨
클라이언트측에서 WaitNmaedPipe(pipeName,20000)을통해 기다릴수있음
FlushFileBuffers 함수는 출력 버퍼를 비우는 역할을 한다. 여기서 비운다는 의미는 목적지로의 전송을 의미이다. 기본족으로 Windows는 파일 시스템을 포함한 댑분의 입/출력 상황에서 버퍼링을 한다. 따라서 이미 전송되었다고 생각되는 데이터들이 목적지의 상황에 따라서 아직까지도 시스템 메모리에 남아 있을 수 있다.
ClostHandle UsageCount의 값을 1줄여줌
DisconnectNamedPipe(hPipe)함수는 클라이언트가 에러 메시지를 받을 수 있도록 도와준다. 이 함수 호출을 통해서 파이프 연결이 끊기고 나면, 클라이언트는 파이프 접근 시 에러가 발생하게 된다. 따라서 클라이언트 역시 이에 따른 적절한 처리가 가능해진다.
-클라이언트측
연결 파이프 이름의 구성
"\servername\pipe\pipename"
클라이언트는 서버 이름을 통해서 원겨게 존재하는 컴퓨터의 파이프에 연결을 한다. 그래서 클라이언트 코드의 파이프 이름 구성에는 서버 이름이 필요하다.
BOOL WaitNamedPipe (
LPCTSTR lpNamedPipeName // 상태 확인의 대상이 되는 파이프 이름을 지정
DWORD nTimeout //타임아웃 시간을 설정 이 시간이 지나서 함수를 빠져나올 경우 FALSE가 반환됨
파이프의 속성을 변경시키는 함수 SetNamedPipeHandleState
1.hNamedPipe : 파이프와의 연결 속성을 변경시키기 위한 핸들 지정.
2.lpMode : 읽기 모드와 함수 리턴방식에 대한 값을 OR 연산하여 전달한다. CreateNamedPipe함수의 전달인자와 같은 이름으로 구성된다.
3.lpMaxCollectionCount : 서버로 데이터를 보내기에 앞서서 버퍼링할 수 있는 최대 바이트 크기를 지정하는데 사용, NULL을 전달해서 이 값을 지정하지 않아도 된다. 만약에 클라이언트와 서버가 같은 PC상에서 동작한다면 반드시 NULL을 전달해야한다.
4.lpCollectionDataTimeout : 서버로 데이터를 보내기에 앞서서 버퍼링을 허용하는 최대 시간을 지정하는데 사용된다. 세 번째 전달인자가 NULL이었다면 NULL을 전달할 수 있으며, 클라이언트와 서버가 같은 PC상에서 동작한다면 반드시 NULL을 전달해야한다.
프로세스 환경 변수
프로세스별로 독립된 메모리 공간에 환경변수가 저장됨
환경변수는 Key와 Value의 쌍의 형태로 저장됨
Set과 Get을 통해 넣고 가져오고 함
Get함수를 호출하면 키를 통해 밸류값을 가져올수있음
CreateProcess 의 4번쨰인자를 NULL로 주면 부모프로세스의 환경변수가 자식에게 그대로 등록이됨
이것만은 알고 갑시다.
1.핸들 테이블
커널 오브젝트와 핸들 사이에 핸들 테이블이 존재한다 이로서 핸들을 참조하여 특정 커널 오브젝트를 가리킬 수 있다.
2.핸들과 핸들 테이블
핸들 테이블은 프로세스별로 독립적이다. 그리고 숫자가 핸들로서 의미를 지니기 위해서는 해당 숫자가 핸들 테이블에 등록되어야 한다. 등록되는 순간부터 핸들이라 할 수 있으며 이 핸들을 통해서 커널 오브젝트에 접근하는 것이 가능하다.
3.핸들의 상속
핸들은 자식 프로세스를 생성하는 과정에서 상속할 수 있다. 핸들이 자식 프로세스에게 상속된다는 말은 부모 프로세스의 핸들 테이블 정보가 자식 프로세스의 핸들 테이블에 복사된다는 뜻이다.
4.가짜 핸들(Pseudo 핸들)
GetCurrentProcess 함수 호출을 통해서 얻은 핸들을 가리켜 가짜 핸들이라 한다. 이는 핸들 테이블에 등록된 핸들값이 아닌, 자기 자신의 프로세스를 가리키기 위한 약속된 상수이기 때문이다. 핸들 테이블에 등록된 핸들을 얻기 위해서는 DuplicateHandle 함수를 사용해야 한다.
5.파이프
이름없는 파이프와 이름있는 파이프를 소개하였다. 사용방법은 예제를 참조하면 되지만, 각각의 특성은 머리 속에 기억하고 있어야 한다.