Vienna
Chapter 6) 커널 오브젝트와 오브젝트 핸들 - 커널 오브젝트에 대한 이해 본문
* 커널 오브젝트란?
커널에서 관리하는 중요한 정보를 담아둔 데이터 블록
Windows 운영체제는 프로세스, 쓰레드 혹은 파일과 같은 리소스들을 원활히 관리하기 위해 필요한 정보를 저장해야 한다.
단, 커널 오브젝트의 종류에 따라서 서로 다른 구조체를 기반으로 생성된다.
* 오브젝트 핸들을 이용한 커널 오브젝트의 조작
프로그래머가 직접 커널 오브젝트를 조작할 수는 없으나, 함수 호출에 의한 간접적인 조작까지 불가능한 것은 아님.
◇ 프로세스의 우선순위 변경
///<summary>
/// hProcess가 가리키는 프로세스의 우선순위를 dwPriorityClass로 변경시킨다
///</summary>
BOOL SetPriorityClass(
HANDLE hProcess, // 1
DWORD dwPriorityClass // 2
);
1. hProcess: 우선순위를 변경할 프로세스의 *핸들(Handle)을 전달
2. dwPriorityClass: 새롭게 적용할 우선순위 정보를 전달
* 커널 오브젝트에 할당되는 숫자
◇ 핸들을 얻는 방법?
GetCurrentProcess() 함수를 사용하면 얻을 수 있다.
/*
* Operation1.cpp
*
* 프로그램 설명: 단순 출력 프로그램1
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
TCHAR command[] = _T("Operation2.exe");
CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
while (1) {
for (DWORD i = 0; i < 1000; i++)
for (DWORD i = 0; i < 1000; i++);
_fputts(_T("Operation1.exe\n"), stdout);
}
return 0;
}
/*
* Operation2.cpp
*
* 프로그램 설명: 단순 출력 프로그램2
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
while (1) {
for (DWORD i = 0; i < 1000; i++)
for (DWORD i = 0; i < 1000; i++);
_fputts(_T("Operation2.exe\n"), stdout);
}
return 0;
}
책의 예제와는 다른 결과가 나오고 있고, 이는 당연한 결과인 것으로 보인다.
책에서는 싱글 코어 환경을 가정하고 있고,
내 CPU는 코어가 12개이기 때문이다.
Operation2 프로세스가 실행되는 동안 Operation1을 실행시킬 코어가 존재하기 때문에 이런 결과가 나온 것으로 예상된다.
* 커널 오브젝트의 종속 관계
1. 커널 오브젝트는 프로세스에 종속적인 것이 아니라, 운영체제에 종속적인 관계
=> 즉, 커널 오브젝트의 소멸시점은 운영체제에 의해 결정된다
2. 커널 오브젝트는 프로세스에 종속되는 것이 아니므로, 여러 프로세스에 의해서 접근이 가능하다.
(물론 함수 호출을 통한 간접 접근)
* 핸들의 종속 관계
커널 오브젝트와는 달리, 핸들은 프로세스에 종속적이다.
A 프로세스가 B 프로세스의 우선순위를 변경시킨다는 것은,
B 프로세스의 커널 오브젝트에 a 프로세스가 접근 가능하다는 뜻이다.
/*
* KerObjShare.cpp
*
* 프로그램 설명: 커널 오브젝트 공유 예제
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
TCHAR command[] = _T("Operation2.exe");
CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
DWORD timing = 0;
while (1) {
for (DWORD i = 0; i < 1000; i++)
for (DWORD i = 0; i < 1000; i++);
_fputts(_T("Parent \n"), stdout);
timing += 1;
if (timing == 2) {
SetPriorityClass(pi.hProcess, NORMAL_PRIORITY_CLASS);
}
}
return 0;
}
싱글 코어가 아니기 때문에 우선순위를 다시 낮춘 것이 무색하게도 별다른 차이점이 없다
typedef struct _PROCESS_INFORMATION{
HANDLE hProcess; // 프로세스의 핸들
HANDLE hThread; // 스레드 핸들
DWORD dwProcessId // 프로세스의 ID
DWORD dwThreadId // 쓰레드 ID
} _PROCESS_INFORMATION;
3. dwProcessId
운영체제는 프로세스를 생성할 때마다 프로세스들을 구분짓기 위한 ID(식별자)를 할당한다.
프로세스 핸들은 프로세스의 커널 오브젝트를 가리키기 위한 것이고,
프로세스 ID는 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위한 것이다.
2,4. hThread, dwThreadId
Windows 운영체제는 프로세스를 생성하면 프로세스 내부적으로 Thread라는 개념의 가벼운 프로세스(Light Weight)를 생성해서 이를 통해 main 함수가 호출되게끔 디자인 되어있다.
* 커널 오브젝트와 Usage Count
◇ CloseHandle 함수와 프로세스 소멸
/*
* KernelObjProb1.cpp
*
* 프로그램 설명: CloseHandle 함수의 이해 1
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
TCHAR command[] = _T("KernelObjProb2.exe");
CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
CloseHandle(pi.hProcess);
return 0;
}
/*
* KernelObjProb2.cpp
*
* 프로그램 설명: CloseHandle 함수의 이해 2
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
DWORD n = 0;
while (n<100) {
for (DWORD i = 0; i < 1000; i++)
for (DWORD i = 0; i < 1000; i++);
_fputts(_T("KernelObjProb2.exe \n"), stdout);
n++;
}
return 0;
}
마찬가지로 책과는 다른 결과가 출력되었다.
/*
* OperationStateParent.cpp
*
* 프로그램 설명: 프로그램 실행 결과에 따른 반환값 확인
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi;
DWORD state;
si.cb = sizeof(si);
si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE;
si.dwX = 100;
si.dwY = 200;
si.dwXSize = 300;
si.dwYSize = 200;
TCHAR command[] = _T("OperationStateChild.exe");
CreateProcess(NULL, command, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
for (DWORD i = 0; i < 10000; i++)
for (DWORD i = 0; i < 10000; i++);
// WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &state);
if (state == STILL_ACTIVE) {
_tprintf(_T("STILL_ACTIVE\n\n"));
}
else {
_tprintf(_T("state: %d\n\n"), state);
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
/*
* OperationStateChild.cpp
*
* 프로그램 설명: 프로그램 실행 결과에 따른 반환값
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
float num1, num2;
_fputts(_T("Return Value Test\n"), stdout);
_tscanf(_T("%f %f"), &num1, &num2);
if (num2 == 0) {
exit(-1); // return -1;
}
_tprintf(_T("Operation Result: %f\n"), num1 / num2);
return 1;
}
즉, 부모 프로세스는 자식 프로세스의 종료 코드를 얻을 수 있다.
부모 프로세스는 종료 코드를 통해 정상적으로 프로그램이 종료되었는지,
비정상적인 동작으로 인해 종료되었는지를 판별할 수 있다.
* 커널 오브젝트와 Usage Count
자식 프로세스의 종료코드는 자식 프로세스의 커널 오브젝트에 저장된다
즉, 자식 프로세스가 종료될 때 커널 오브젝트도 동시에 소멸된다면
부모 프로세스는 종료 코드를 얻을 수 없게 된다.
◇ 그렇다면 커널 오브젝트를 소멸시키는 시점은?
커널 오브젝트를 참조하는 대상이 하나도 없을 때 소멸시키는 것이 가장 이상적이며,
Winodws가 커널 오브젝트를 소멸시키기를 결정하는 방식.
그래서 Windows는 커널 오브젝트를소멸시키기를 결정짓기 위해 UsageCount(참조 횟수)를 관리한다.
UsageCount가 0이 되는 순간, 해당 커널 오브젝트는 소멸된다.
'그외 > 뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
Chapter 7) 프로세스 간 통신(IPC) 1 - 메일슬롯 방식의 IPC (0) | 2023.04.19 |
---|---|
Chapter 7) 프로세스 간 통신(IPC) 1 - IPC의 의미 (0) | 2023.04.18 |
Chapter 18) 파일 I/O와 디렉터리 컨트롤 - 파일 열기/닫기/읽기/쓰기 (0) | 2023.04.17 |
Chapter 5) 프로세스 생성과 소멸 - 프로세스의 스케줄링과 상태 변화 (1) | 2023.04.15 |
Chapter 5) 프로세스 생성과 소멸 - 프로세스의 이해 (0) | 2023.04.15 |