VI : 커널 오브젝트와 오브젝트 핸들
뇌를 자극하는 윈도우즈 시스템 프로그래밍 을 읽고 정리한 문서입니다 ;)
커널 오브젝트에 대한 이해
커널이란 컴퓨터를 운영하는 데 있어서 중심이 되는 운영체제 핵심 부분을 뜻한다. 커널이라는 용어와 운영체제라는 용어는 같은 의미로 쓰이기도 한다.
커널 오브젝트는 커널 메모리 영역에서 관리되는 중요한 정보를 담아둔 데이터 블록을 의미한다.
운영체제가 프로세스를 관리하기 위해서는 프로세스에 관련된 몇몇 정보를 저장할 수 있어야 하고, 참조 및 변경도 가능해야 한다. 따라서 이러한 내부적인 구조체를 생성하고 관리에 필요한 데이터로 초기화하여 커널메모리에서 관리하는데, 이것이 커널 오브젝트이다.
프로세스가 생성될 때에만 커널 오브젝트가 생기는 것이 아닌, 스레드를 생성할 때에도, 파이프나 메일슬롯을 생성할 때에도, 파일을 만들 때에도 모두 커널 오브젝트가 생성된다. 프로세스 내부가 아닌 윈도우에서 관리하는 리소스이기 때문이다.
Windows 운영체제는 프로세스, 스레드 혹은 파일과 같은 리소스들을 원활히 관리하기 위해 필요한 정보를 저장해야 하는데, 이 데이터를 저장하는 메모리 블럭을 가리켜 커널 오브젝트라고 한다.
이러한 커널 오브젝트는 프로그래머가 직접 조작할 수 없고, 윈도우를 통해 제공해 주는 시스템 함수를 이용해서 간접적으로 조작할 수 있다.
예를 들어, 프로세스 우선순위를 높이는 SetPriorityClass()
함수의 첫 번째 인자는 HANDLE
로서, 이때 핸들은 커널 오브젝트에 할당되는 숫자에 지나지 않는다.
윈도우는 커널 오브젝트를 생성할 때마다 핸들이라는 정수값을 하나씩 부여하기 때문에, 이러한 정수값 하나만을 가지고도 커널 오브젝트들을 구분하고 명시할 수 있게 된다.
커널 오브젝트와 핸들의 종속 관계
커널 오브젝트는 Windows 운영체제에 종속적이다. 즉, 커널 오브젝트는 프로세스에 종속적인 것이 아니기에, 커널 오브젝트의 소멸 시점은 운영체제에 의해 결정된다는 것이다.
또한, 커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영체제에 종속적인 관계로, 여러 프로세스에 의해서 접근이 가능하다.
핸들은 운영체제에 종속적이지 않고 프로세스에 종속적이다. 커널 오브젝트와 핸들을 구분하자.
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION
프로세스 핸들은 프로세스의 커널 오브젝트를 가리키기 위한 것이고, 프로세스 ID는 커널 오브젝트가 아닌 프로세스 자체를 구분짓기 위한 것이다.
커널 오브젝트와 Usage Count
CreateProcess()
함수 호출이 커널 오브젝트 생성의 원인이기는 하지만, 이 요청을 처리하는 과정에서 운영체제가 관리의 편의를 위해 커널 오브젝트가 생성한 것이다.
프로세스가 소멸된다면 커널 오브젝트가 소멸된다고 말할 수 있을까? 그럴 수도 있고, 그렇지 않을 수도 없다. 운영체제가 전적으로 결정하기 때문이다.
CloseHandle()
함수는 함수 이름처럼 핸들을 닫는 기능을 하고 있다. 즉, 핸들을 반환하는 것이다.
하지만 이것이 핸들이 가리키는 리소스가 필요 없으니 그에 해당하는 리소스와 커널 오브젝트를 소멸시키라는 명령으로 해석되는 것은 옳지 않다.
이를 위해서는 운영체제가 커널 오브젝트 소멸시점을 결정하는 방법을 알아봐야 하는데, 이는 해당 커널 오브젝트를 참조하는 대상이 하나도 없을 때 소멸시키는 윈도우의 동작 원리를 알아야 한다.
main()
문에서 일반적으로 종료코드 -1
이나 0
은 비정상적인 종료를 알리고자 많이 사용하는데, 이러한 자식 프로세스의 종료코드는 어디에 저장될까? 바로 자식 프로세스의 커널 오브젝트에 저장된다.
따라서 이를 얻기 위해서는 자식 프로세스가 종료된 이후 부모 프로세스가 자식 프로세스의 커널 오브젝트로부터 읽어와야 하기에, 바로 자식 프로세스 커널 오브젝트를 소멸시킬 수 없다는 뜻이다.
윈도우는 이러한 정책을 기반으로 커널 오브젝트 소멸시기를 결정짓기 위해 Usage Count, 참조 횟수라는 것을 관리한다. 이 usage count가 0이 되는 순간 해당 커널 오브젝트는 소멸된다.
이 Usage Count 또한 해당 커널 오브젝트의 멤버로 존재한다.
이제 다시 CloseHandle()
함수를 정의해보자면, 핸들을 반환하면서 커널 오브젝트의 Usage count를 하나 감소시키는 기능을 가진다.
만일 자식 프로세스를 CreateProcess()
를 통해 생성하자마자 CloseHandle()
로 핸들을 반환한다면, 자식 프로세스의 커널 오브젝트 Usage Count는 1
이 된다. 따라서 자식 프로세스 종료 시 Usage count가 0이 되어 바로 소멸될 수 있게 된다. 이는 곧 해당 프로세스의 메인 쓰레드에서도 동일한 문제로 작용되기에, CloseHandle()
을 스레드에 대해서도 관리해주어야 한다.
이것만은 알고 갑시다
- 커널 오브젝트와 핸들
- 커널 오브젝트는 윈도우가 리소스를 관리할 수 있도록 커널 영역 메모리에 저장해놓은 메모리 블럭을 의미하고, 핸들은 이 메모리 공간을 가리킬 수 있는 프로세스 종속적 번호표이다.
- 운영체제에 종속적인 커널 오브젝트
- 커널 오브젝트는 운영체제에 종속적이기에, 커널 오브젝트의 생성과 소멸은 운영체제에 의해 관리된다. 그렇기에 윈도우가 제공하는 함수로만 간접 제어가 가능하며, 핸들을 사용한다.
- Usage Count
- 커널 오브젝트의 소멸 시기는 아무도 커널 오브젝트를 참조하지 않을 때이다. 즉, 이를 알기 위해서는 커널 오브젝트를 참조하는 핸들의 개수를 알아야 하고, 이것이 Usage Count이다.
CloseHandle()
- 이는 핸들을 반납하고, Usage Count를 하나 줄이는 함수이고, 결코 해당 핸들이 가리키는 리소스나 커널 오브젝트의 동작에 직접적인 제어를 하는 것이 아니다.
- 프로세스는 생성과 동시에 Usage Count가 2가 된다.
- 이는 프로세스를 키도록 하는 부모 프로세스가 있기 때문이고, (Explorer.exe 등). 그렇기 때문에 부모 프로세스가 가진 자식 프로세스 핸들 + 자식의 고유 프로세스 핸들로 Usage Count는 2개로 시작된다.
- 종료코드
- 프로세스의 종료코드는 일반적으로
-1
,0
을 통해 에러를 알리며,1
로 정상 종료를 알린다. 이는 해당 프로세스의 커널 오브젝트의 멤버로서 존재하기에, 부모 프로세스가 이를 읽기 위해서는 핸들이 필요할 것이다.
- 프로세스의 종료코드는 일반적으로
'공부한 이야기 > 윈도우 OS' 카테고리의 다른 글
VIII : 프로세스간 통신 (IPC) 2 (0) | 2023.04.29 |
---|---|
VII : 프로세스간 통신 (IPC) 1 (0) | 2023.04.29 |
V : 프로세스의 생성과 소멸 (0) | 2023.04.29 |
IV : 컴퓨터 구조에 대한 두번째 이야기 (0) | 2023.04.29 |
III : 64비트 기반 프로그래밍 (0) | 2023.04.29 |