[스터디] 시스템 프로그래밍 - Chapter 6. 커널 오브젝트와 오브젝트 핸들

2023. 10. 3. 23:01·독서/뇌를 자극하는 윈도우즈 시스템 프로그래밍

뇌를 자극하는 윈도우즈 시스템 프로그래밍(저자, 윤성우)


01. 커널 오브젝트에 대한 이해

커널은 컴퓨터를 운영하는 데 있어서 중심이 되는 운영체제 핵심 부분을 뜻한다. 일반적으로 커널과 운영체제라는 용어들은 같은 의미로 사용되기 때문에 운영체제에서 커널이 차지하는 영역을 굳이 구분할 필요는 없다. 그러나 커널 오브젝트는 이와 달리, 고유 명사격으로 사용되기 때문에 운영체제 오브젝트라는 명칭은 나타날 수 없다.

 

커널 오브젝트는 커널에서 관리하는 중요한 정보들을 담아둔 데이터 블록을 가리킨다.

 

앞서 생성한 프로세스의 생성 주체는 Windows 운영체제다. 또, 생성된 프로세스를 관리하는 것도 운영체제의 몫이다. 관리의 범위는 프로세스 생성, 소멸, 그리고 사이에 일어나는 모든 일들이 포함된다. 이렇게 동시에 프로세스들을 관리하기 위해서는 운영체제는 고정적으로 저장하고 갱신해야 하는 정보들이 생기기 마련이다.

 

예시로, 프로세스 상태 정보와 우선순위 정보를 들 수 있다. 운영체제 내부에 저장이 되어야 프로세스 스케줄러가 해당 정보들을 바탕으로 프로세스를 관리할 수 있기 때문이다.

 

이를 위해서 프로세스 상태 정보를 저장하기 위해 Windows 운영체제 개발자들은 프로세스 관리 구조체를 정의했다. 프로세스 생성 시, 변수가 하나씩 생성되고 새롭게 생성된 프로세스 정보들로 초기화하는데 이것이 커널 오브젝트Kernel Object이다.

 

 

01. A. 그 이외의 커널 오브젝트들

프로세스 생성시에만 커널 오브젝트가 생성되는 것은 아니고, 프로그램 흐름을 구성하는 쓰레드 생성시, IPC(Inter Process Communication)를 위해 사용되는 파이프나 메일 슬롯을 생성할 때에도 커널 오브젝트를 생성한다. 뿐만 아니라, Windows에서는 파일을 생성할 때에도 커널 오브젝트를 생성한다. 파일도 커널에 의한 관리 대상이기 때문이다.

 

그리고 커널 오브젝트들은 동일한 구조체로 생성되지 않는다. 종류에 따라 서로 다른 구조체를 기반으로 생성된다. 각자 사용하는 정보들이 다르기 때문이다. 앞선 내용을 요약하면 다음과 같다.

 

Windows 운영체제는 프로세스, 쓰레드, 파일과 같은 리소스Resource들을 원활히 관리하기 위해 필요한 정보를 저장한다. 이때 데이터를 저장하는 메모리 블록을 가리켜 커널 오브젝트라 한다.

 

01. B. 오브젝트 핸들Handle을 이용한 커널 오브젝트의 조작

커널 오브젝트는 직접 접근해서 사용하는 것이 아니라, MS에서 제공하는 함수 호출을 통해서 간접적인 조작을 통해서 사용할 수 있다. 여태 학습한 내용이 프로세스이니, 그에 관련된 커널 오브젝트 조작 함수를 살펴 보자.

 

- 프로세스의 우선순위Priority 변경

BOOL SetPriorityClass (
    HANDLE hProcess,       // 우선순위를 변경할 프로세스의 핸들을 전달한다.
    DWORD dwPriorityClass  // 새롭게 적용할 우선순위 정보를 전달한다.
);

위에서 설명하는 핸들이란, 커널 오브젝트에 할당되는 숫자에 지나지 않는다. 

 

- 커널 오브젝트에 할당되는 숫자, 핸들Handle

특정 프로세스의 우선순위를 변경하기 위해서는 우선순위 정보가 존재하는 커널 오브젝트를 가져와야 한다. 이때, Window에서 커널 오브젝트를 구분하기 위해서 부여하는 정수값이 핸들이다.

 

- 핸들 정보는 어디서 얻을 수 있나?

핸들 정보는 커널 오브젝트의 종류에 따라서 다양하다. 아래의 예제에서 프로세스의 핸들을 얻는 방법을 살펴보자.

 

// Operation1.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
	);

	while (1)
	{
		for (DWORD i = 0; i < 10000; i++)
		{
			for (DWORD i = 0; i < 1000; i++);
		}

		_fputts(_T("Operation1.exe \n"), stdout);
	}

	return 0;

}
// Operation2.cpp

#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 < 10000; i++)
		{
			for (DWORD i = 0; i < 1000; i++);
		}

		_fputts(_T("Operation2.exe \n"), stdout);
	}

	return 0;
}

예제는 싱글코어 CPU 기반에서 이루어지는 것을 가정하고 있는데 아쉽게도 내 컴퓨터는 싱글코어 CPU가 아니라서 예제에서 기대하는 실행 결과가 나오지 않았다. 예제에서 기대하는 실행 결과는 Operation2의 우선순위를 높혔기에 Operation2.exe 텍스트가 뜨다가 Operation1.exe 텍스트가 간혈적으로 나타난다.

 

▶ 실행 결과가 정상적으로 나타날 경우

: 간혹, Operation1 글자가 한번에 출력되지 않고 나눠서 나타날 수 있다. 이를 통해서 함수 호출이 완료되기 전에 다른 프로세스에게 CPU 할당 시간을 넘겨주는 것을 알 수 있다.

 


02. 커널 오브젝트와 핸들의 종속 관계

02. A. 커널 오브젝트의 종속 관계

커널 오브젝트는 프로세스가 아니라 Windows 운영체제에 종속적이다. 이로 인해서 커널 오브젝트의 소멸 시점이 운영체제에 의해서 결정됨을 알 수 있다. 또, 커널 오브젝트는 함수 호출을 통한 간접 호출을 가지고 여러 프로세스에서 접근할 수 있다. 

 

02. B. 핸들의 종속 관계

- 커널 오브젝트의 공유 예제

커널 오브젝트는 여러 프로세스에 의해서 접근될 수 있다고 했다. 'A 프로세스가 B 프로세스를 생성한다. B 프로세스는 자신의 우선순위를 높인다. A 프로세스는 이후, B 프로세스의 우선순위를 원래대로 돌려둔다.'란 예제를 가지고 살펴보자.

 

// 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<10000; i++)
            for(DWORD i=0; i<10000; i++);


            _fputts(_T("Parent \n"), stdout);

        timing += 1;
        if (timing == 2)
            SetPriorityClass(pi.hProcess,NORMAL_PRIORITY_CLASS);
    }
    
    return 0;
}

 

Operation2.cpp 예제와 함께 실행해서 보면 Parent와 Operation2.exe가 번갈아 나오는 것을 확인할 수 있다. 만약에 Operation2 프로세스의 우선순위가 높게 설정이 되어있다면 번갈아 나오는 것이 아니어야 한다.

 

이는 KerObjShare.cpp에서 Operation2 프로세스의 우선순위를 변경하고 있음을 시사한다. 이때, 우선순위가 동일하다면 출력되는 양이 동일해야 한다고 생각되겠지만 스케줄링 알고리즘에 따라 변경되는 부분이라, 동일하게 출력되지는 않는다.

 

- PROCESS_INFORMATION 구조체

이전에 가볍게 언급한 PROCESS_INFORATMION의 정의를 보자. 

 

typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;    // 프로세스의 핸들
    HANDLE hThread;     // 쓰레드 핸들
    DWORD dwProcessId;  // 프로세스 ID
    DWORD dwThreadId;   // 쓰레드 ID
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

운영체제는 프로세스 생성 시, 구분을 위한 ID(식별자)를 할당한다. dwProcessId에는 새로 생성되는 프로세스ID 정보로 채워지게 된다. 프로세스 핸들과 프로세스 ID의 차이점이 모호하다고 생각할 수 있는데 프로세스 핸들은 프로세스의 커널 오브젝트를 가르키기 위한 것이고 프로세스 ID는 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위한 것이라고 알고 넘어가자.

 

이제는 쓰레드 핸들과, 쓰레드 ID를 살펴보자. Windows 운영체제는 프로세스를 생성하면, 내부적으로 쓰레드라는 개념의 가벼운Weight 프로세스를 생성하여 main 함수가 호출되게끔 디자인되어 있다.

 

CreateProcess 함수를 통해 프로세스를 생성하면 쓰레드라는 시스템 리소스도 같이 생성되는 것이다. 이 쓰레드의 핸들과 ID 정보가 각각 알맞는 인자에 채워지게 된다.

 


03. 커널 오브젝트와 Usage Count

커널 오브젝트의 생성 주체는 운영체제다. 그렇기 때문에 커널 오브젝트는 프로세스의 생성 및 소멸에 직접적인 영향이 없다. 단순히 운영체제에서 같이 생성하고 필요에 따라 소멸시키기 때문이다.

 

BOOL CloseHandle (
	HANDLE hObject
);

이번에 살펴볼 CloseHandle 함수의 정의다. CloseHandle 함수는 이름처럼 핸들을 닫는 기능을 가지고 있다. 이를 핸들을 반환한다는 표현을 가지기도 한다. 간혹 CloseHandle 함수의 역할이 리소스를 해제하고 커널 오브젝트를 소멸시키는 것으로 설명되는 경우가 있는데 단순히 핸들의 반환을 한다.

 

03. A. CloseHandle 함수와 프로세스 종료 코드

커널 오브젝트의 소멸 시점에서 전제 조건은 프로세스가 종료되어야 한다는 것이다. 하지만 종료 직후, 커널 오브젝트를 소멸시키면 문제가 발생할 수 있다. 

 

어떠한 문제인지 알기 위해서 종료 코드에 대해서 살펴봐야 한다. 코드의 끝 부분에서 우리가 자주 작성하는 return 문을 보면 0 혹은 1을 반환한다. 이는 종료 상태를 알리는 값들이다. 이러한 상태의 대표적인 예시는 HTTP 코드이다. 흔히 우리가 알고 있는 404 Not Found가 여기에 해당한다.

 

https://datatracker.ietf.org/doc/html/rfc7231

HTTP 통신에서는 숫자에 의미를 부여하여 사용자들이 어떤 상태인지를 파악할 수 있다. 200번대의 번호는 정상적으로 실행이 된 것이고 300번대의 번호는 리다이렉션에 관련된 번호다. 이런식으로 번호에 의미를 부여하는 것은 종료 상태에서도 통용된다.

 

다시 본론으로 돌아와서 그렇다면, 자식 프로세스의 종료 코드는 어디에 저장이 될까? 당연하게도 자식 프로세스의 커널 오브젝트다. 그렇기에 프로세스 소멸 시, 프로세스에 해당하는 커널 오브젝트도 소멸시킨다면 부모 프로세스의 입장에서는 종료 코드를 알 수 없다.

 

그렇기에 커널 오브젝트의 가장 이상적인 소멸 시점은 커널 오브젝트를 참조하는 대상이 없을 때이고, 이는 Windows에서 커널 오브젝트 소멸을 결정하는 방식이다. Windows에서는 이러한 소멸 방식을 관리하기 위해서 Usage Count(참조 횟수)라는 것을 관리한다. Usage Count가 0이 되는 순간 커널 오브젝트를 소멸시킨다.

 

▶ Usage Count와 CloseHandle 함수

: Usage Count에 대해서 설명하였으니, CloseHandle 함수에 대해서 자세히 이해할 수 있게 됐다. CloseHandle 함수는 핸들을 반환하면서 Usage Count를 하나 감소 시키는 기능을 한다.
▶ 프로세스 강제 종료

: 정확히 프로세스를 종료하는 역할을 하는 함수는 TerminateProcess 함수다. 프로세스의 상태를 고려하지 않고 종료하기 때문에 부모 프로세스라고 하더라도 자식 프로세스를 함부로 종료하는 것은 좋지 않다. 가장 이상적인 종료는 프로그램이 자신의 역할을 수행하고 return 문을 통해서 종료되는 것이다.

 

'독서 > 뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글

[스터디] 시스템 프로그래밍 - Chapter 8. 프로세스간 통신(IPC) 2  (2) 2023.10.04
[스터디] 시스템 프로그래밍 - Chapter 7. 프로세스간 통신(IPC) 1  (2) 2023.10.04
[스터디] 시스템 프로그래밍 - Chapter 5. 프로세스의 생성과 소멸  (0) 2023.10.02
[스터디] 시스템 프로그래밍 - Chapter 4. 컴퓨터 구조에 대한 두 번째 이야기  (1) 2023.09.19
[스터디] 시스템 프로그래밍 - Chapter 3. 64비트 기반 프로그래밍  (1) 2023.09.13
'독서/뇌를 자극하는 윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
  • [스터디] 시스템 프로그래밍 - Chapter 8. 프로세스간 통신(IPC) 2
  • [스터디] 시스템 프로그래밍 - Chapter 7. 프로세스간 통신(IPC) 1
  • [스터디] 시스템 프로그래밍 - Chapter 5. 프로세스의 생성과 소멸
  • [스터디] 시스템 프로그래밍 - Chapter 4. 컴퓨터 구조에 대한 두 번째 이야기
태역
태역
  • 태역
    RYULAB
    태역
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 언어
        • C
        • C++
        • C#
      • 엔진, 프레임워크
        • Unity
        • Unreal
        • Electron
      • 공부
        • 디자인 패턴
        • 수학
        • CS
        • Git
        • 알고리즘
        • 자료구조
      • 코테
        • 프로그래머스
        • 백준
      • 독서
        • Effective C#
        • CLR via C#
        • 뇌를 자극하는 윈도우즈 시스템 프로그래밍
        • 오브젝트
        • CSAPP
        • OSTEP
      • 프로젝트
        • Unity
      • 개발 일지
        • 퓨처리티
        • 골든타임
      • 활동
        • 게임잼 후기
        • 게임제작동아리 브릿지
        • 크래프톤 정글
        • 기타
      • 기타
  • 블로그 메뉴

    • 링크

    • 공지사항

      • 2024 04 17
    • 인기 글

    • 태그

      인프런 #인프런강의후기 #게임개발 #게임개발강의 #인강후기 #강의후기 #게임개발자 #인프런강의
      티스토리챌린지
      오블완
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    태역
    [스터디] 시스템 프로그래밍 - Chapter 6. 커널 오브젝트와 오브젝트 핸들
    상단으로

    티스토리툴바