Recent Posts
Recent Comments
Link
11-23 13:47
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

kernel object and object handle 본문

DEV&OPS/C & C++

kernel object and object handle

ALEPH.GEM 2021. 9. 10. 18:14

커널 오브젝트(kernel object)

프로세스나 스레드, 파일 같은 리소스를 관리하기 위한 정보들을 모아놓은 구조체가 메모리에 로딩되어 블록 형태로 존재하고 있는데 이를 가리켜 커널 오브젝트라고 부릅니다.

각각의 리소스 별로 따로 커널 오브젝트들이 존재하게 됩니다.

예를들어 하나의 프로세스가 생성되면 그 프로세스를 위한 커널 오브젝트가 운영체제에 의해 생성됩니다.

 

오브젝트 핸들(kernel object handle)

운영체제는 커널 오브젝트의 간접적인 조작을 위해 여러 가지 시스템 함수를 제공합니다.

오브젝트 핸들은 커널 오브젝트 식별을 위해 할당되는 값를 말합니다. 

 

int _tmain(int argc, TCHAR* argv[]){
//GetCurrentProcess(): 현재 프로세스의 오브젝트 핸들을 리턴
//HIGH_PRIORITY_CLASS: 일반적인 프로세스보다 우선순위를 높여주는 상수 값
//NORMAL_PRIORITY_CLASS: 일반적인 프로세스의 우선순위를 나타내는 상수 값
//현재 프로세스의 우선순위를 높임
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);	

...

}

 

프로세스(리소스)와 커널 오브젝트의 종속 관계

커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영 체제에 종속되어 커널 오브젝트의 관리는 운영체제가 결정합니다. 

따라서 커널 오브젝트는 시스템 함수를 통해 여러 프로세스들에 의해 접근이 가능합니다.

반대로 핸들은 프로세스에 종속적입니다. 프로세스들 마다 각각의 핸들이 부여되기 때문입니다.

예를 들어 A프로세스가 B프로세스를 생성한 뒤 A프로세스는 B프로세스의 우선순위를 변경할 수 있습니다.

이는 하나의 커널 오브젝트에 둘 이상의 프로세스가 접근 가능하다는 뜻입니다.

 

PROCESS_INFORMATION 구조체

typedef struct _PROCESS_INFOMATION
{
	HANDLE hProcess;
 	HANDLE hThread;
	DWORD dwProcessId;
	DWORD dwThreadId;
}

여기서 프로세스 핸들(hProcess)은 커널 오브젝트를 식별하기 위한 것이고, 프로세스 id(dwProcessId)는 프로세스 자체를 식별하기 위한 것입니다.

 

프로세스와 커널 오브젝트의 소멸

프로세스가 생성되면 그 프로세스를 위한 커널 오브젝트가 생성되지만, 프로세스가 소멸한다고 해서 커널 오브젝트가 반드시 소멸하지는 않습니다.

자식 프로세스의 종료코드(main함수의 return값)는 자식 프로세스의 커널 오브젝트에 저장됩니다. 

만약 자식 프로세스가 종료될 때 커널 오브젝트도 같이 소멸된다면 부모 프로세스는 자식 프로세스의 종료 코드를 얻을 수 없게 됩니다.

그래서 커널 오브젝트를 참조하는 프로세스가 하나도 없게 되어야 운영체제가 커널 오브젝트를 소멸시키는 것입니다.

부모 프로세스가 자식 프로세스 핸들을 참조하고 있다면 자식 프로세스가 종료되어도 자식 프로세스의 커널 오브젝트는 소멸되지 않고 살아있다는 말입니다.

1. 부모 프로세스가 자식 프로세스를 생성하면 동시에 자식 프로세스에 대한 커널 오브젝트의 Usage Count 값이 1이 됩니다. 

2. 부모 프로세스에서 CreateProcess()함수를 호출하는 과정에서 PROCESS_INFORMATION을 통해 자식 프로세스의 핸들을 획득하기 때문에 자식 프로세스 생성이 완료되면 커널 오브젝트의 Usage Count가 1이 증가해서 2가 됩니다. 

그래서 자식 프로세스가 1개 생성 되었을 때 자식 프로세스의 커널 오브젝트 Usage Count는 2인 것입니다. 

즉, 자식 프로세스의 커널 오브젝트는 부모 프로세스도 참조하고 있고 자식 프로세스 자신도 참조하고 있는 것입니다.

따라서, 이와같은 이유들로 인해 프로세스 종료 시점에 커널 오브젝트가 같이 소멸되는 것이 아닌, Usage Count 가 0이 되는 시점이 커널 오브젝트가 소멸되는 시점인 것입니다.

 

CloseHandle

프로세스 핸들을 반환하는 함수가 바로 CloseHandle()입니다. 

BOOL CloseHandle(HANDLE hObject);

주의할 점이 커널 오브젝트를 소멸(close)시키는 것이 아닌 프로세스 핸들을 반환하는 것뿐입니다. 

부모 프로세스가 자식 프로세스를 하나 생성하면 Usage Count(참조 횟수)가 2가 된다는 것은 위에서 설명했습니다.

여기서 자식 프로세스가 종료되면 자신의 핸들이 소멸되어 자식 프로세스의 커널 오브젝트에서의 Usage Count 값이 1이 감소하면서 종료 코드를 자신의 커널 오브젝트에 저장합니다.

이 상태에서 부모프로세스에서 CloseHandle(자식 프로세스의 핸들)을 호출하게 되면 부모 프로세스는 자식 프로세스의 오브젝트 핸들을 반환하므로 Usage Count 가 하나 더 감소하여 Usage Count는 0이 됩니다.

즉, CloseHandle함수는 핸들을 반환함과 동시에 Usage Count를 하나 감소하는 기능을 합니다.

1. 프로세스가 종료되는 시점에서 Usage Count가 1감소

2. CloseHandle 함수가 호출되면서 Usage Count가 1 감소

 

바탕화면(Explorer.ere: 탐색기) 자체도 프로세스이고 더블클릭 이벤트로 프로세스를 생성하기 때문에 바탕화면(탐색기)에서 실행한 프로세스들은 모두 자식 프로세스 들이어서 Usage Count는 2부터 시작하게 됩니다.

 

계산기 예제    

명령 프롬프트에서 4칙연산 수행(1~4)하고 윈도우 계산기를 자식 프로세스로 실행(5)하는 예제입니다.

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

enum{DIV=1, MUL, ADD, MIN, ELSE, EXIT};

DWORD ShowMenu();
void Divide(double, double);
void Multiple(double, double);
void Add(double, double);
void Minus(double, double);

int _tmain(int argc, TCHAR* argv[]){
	STARTUPINFO si={0,};
	//PROCESS_INFORMATION: HANDLE hProcess, HANDLE hThread, DWORD dwProcessId, DWORD dwThreadId 가 정의되어 있는 구조체
	//프로세스 자신의 핸들을 얻기 위해서는 GetCurrentProcess() 함수를 이용한다.
	PROCESS_INFORMATION pi;		//프로세스의 핸들에 접근하기 위해서는 pi.hProcess 처럼 접근 할 수 있다.
	si.cb=sizeof(si);

	TCHAR command[]=_T("calc.exe");	//윈도우계산기(추가로 실행 할 프로세스)
	//윈도우즈는 1. 샐행중 프로세스 경로 2.시스템경로 3.윈도우즈경로 4.환경변수(PATH)에 등록된 경로의 순서로 경로를 탐색한다.
	SetCurrentDirectory(_T("C:\\WINDOWS\\system32"));	//실행할 프로세스의 경로, 시스템 디렉토리 이므로 생략이 가능

	DWORD sel;
	DWORD state;
	double num1, num2;

	while(true) {
		sel=ShowMenu();
		if(sel==EXIT){
			return 0;
		}
		if(sel!=ELSE){
			_fputts(_T("Input Num1 Num2> "), stdout);
			_tscanf(_T("%lf %lf"), &num1, &num2);
		}
		switch(sel){
		case DIV:
			Divide(num1, num2);
			break;
		case MUL:
			Multiple(num1, num2);
			break;
		case ADD:
			Add(num1, num2);
			break;
		case MIN:
			Minus(num1, num2);
			break;
		case ELSE:
			ZeroMemory(&pi, sizeof(pi));	//ZeroMemory(): 구조체변수 pi를 0으로 초기화 해주는 함수
			CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);	//자식 프로세스 생성(윈도우 계산기 실행)
			//자식 프로세스 생성과 동시에 Usage Count가 2가 되므로 자식프로세스(윈도우계산기)를 종료해도 Usage Count값은 1이 됨.
			//따라서 커널 오브젝트는 소멸되지 않은 채 누적될 수 있음.
			CloseHandle(pi.hProcess);	//그래서 CloseHandle 함수를 호출해서 Usage Count 값을 1 감소 시켜서 자식 프로세스 종료시 커널 오브젝트도 함께 소멸 시킴.
			CloseHandle(pi.hThread);	//스레드도 마찬가지로 커널 오브젝트 소멸
			break;
		}
	}

	return 0;
}

DWORD ShowMenu(){
	DWORD sel;
	_fputts(_T("1: Divde \n"), stdout);
	_fputts(_T("2: Multiple \n"), stdout);
	_fputts(_T("3: Add \n"), stdout);
	_fputts(_T("4: Minus \n"), stdout);
	_fputts(_T("5: Execute calc.exe \n"), stdout);
	_fputts(_T("6: Exit \n"), stdout);
	_fputts(_T("Select number>"), stdout);
	_tscanf(_T("%d"), &sel);
	return sel;
}

void Minus(double a, double b){
	_tprintf(_T("%f-%f=%f \n\n"), a,b,a-b);
}

void Add(double a, double b){
	_tprintf(_T("%f+%f=%f \n\n"), a,b,a+b);
}

void Multiple(double a, double b){
	_tprintf(_T("%f*%f=%f \n\n"), a,b,a*b);
}

void Divide(double a, double b){
	_tprintf(_T("%f/%f=%f \n\n"), a,b,a/b);
}

 

5를 입력하면 윈도우 계산기가 실행됩니다.

 

 

 

728x90

'DEV&OPS > C & C++' 카테고리의 다른 글

windows 시스템 함수 호출 오류시 오류코드 획득  (0) 2021.09.07
Polymorphic 자료형  (0) 2021.09.03
MFC String 자료형  (0) 2021.09.01
C++ 문자열 처리(string class)  (0) 2021.09.01