시스템 프로그래밍 6장 - 커널 오브젝트와 오브젝트 핸들

김주현·2021년 10월 4일
1

시스템 프로그래밍

목록 보기
6/21

커널 오브젝트에 대한 이해

커널 : 컴퓨터를 운영하는 데 있어서 중심이 되는 운영체제 핵심 부분을 뜻함
일반적으로 커널이라는 용어와 운영체제라는 용어는 같은 의미로 사용되기 때문에 굳이 운영체제에서 커널이 차지하는 영역을 명확히 구분할 필요는 없다.

커널 오브젝트 : "커널에서 관리하는 중요한 정보를 담아둔 데이터 블록"

파일을 실제 생성하고 관리하는 주체는 OS이다
OS는 관리를 위해 "접근정보,어디까지 읽었는지에 대한 정보"등이 필요한데 이러한 정보들을 담아둔 블록을 커널 오브젝트라고 한다.

리소스를 생성하게 되면 그에 해당하는 커널오브젝트가 생성이됨

각 커넬 오브젝트는 리소스의 특성에따라 가져야하는 정보들이 다름

  • 커널 오브젝트의 이해

프로세스를 생성하고 관리(프로세스 생성과 소멸, 그리고 그 사이에 일어나는 모든 일)하는 실질적 주체는 운영체제임

프로세스 상태정보와 우선순위 정보는 운영체제 내부에 저장되고 갱신되어야함
그래야 프로세스 스케줄러가 이 정보를 바탕으로 프로세스를 관리할 수 있음.

프로세스 관리 구조체 : 프로세스 상태 정보를 저장하기 위해 정의된 구조체

프로세스가 생성될 떄마다 "프로세스 관리 구조체" 변수가 하나씩 생성되고, 새롭게 생성된 프로세스 정보들로 초기화 되는데 이것이 바로 커널 오브젝트의 정체이다.

구조체 이름을 _PObject로 표시하고 있지만 이는 가정이다 어떻게 정의 되어있는지를 문서화한 것은 없다.

  • 그이외의 커널 오브젝트들

프로세스가 생성될 때에만 커널 오브젝트가 생성되는 것은 아니다. 프로세스 내에서 프로그램 흐름을 구성하는 쓰레드를 생성할 때에도, IPC를 위해 사용되는 파이프나 메일슬롯을 생성할 때에도 커널 오브젝트를 생성해서 필요한 정보들을 채워야만한다. 그래야 운영체제가 이들을 관리할 수 있다. 뿐만 아니라 윈도우즈에서는 파일을 생성할 때에도 커널 오브젝트가 생성된다. 파일조차도Windows 커널에 의한 관리 대상이기 때문이다.

커널오브젝트는 종류에 따라서 서로 다른 구조체를 기반으로 생성된다.

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


다음 그림은 커널 오브젝트 여러 개가 생성된 상태를 보여준다. 여기서 강조하고 있는 것은 Windows 커널에 의해서 관리되는 리소스 수만큼 커널 오브젝트도 생성된다는 사실이다.

  • 오브젝트 핸들을 이용한 커널 오브젝트의 조작

커널오브젝트는 OS에서 관리하는 오브젝트 프로그래머가 직접 조작할수없음

프로그래머가 직접 커널 오브젝트를 조작할 수는 없지만 함수 호출로 간접적으로 조작할수는 있다.

함수를 통해 커널 오브젝트를 조작하려고 할때 그냥 함수를 호출하는것이 아닌 어떤 프로세스를 조작하고 싶은것인지 넘겨줘야함(프로세스가 하나가아닌 여러개가 실행중이기 떄문) 그래서 커널 오브젝트를 가리키는 핸들 변수를 넘겨줌

커널 오브젝트에 직접적인 접근은 안되지만 핸들을 통해 OS에 요청할수있음

윈도우즈에서 관리하는 리소스 특성을 변경시키기 위해서는 해당 리소스의 커널 오브젝트를 조작해야만 하는데. 직접적인 조작은 불가능 하지만 마이크로 소프트에서 제공해 주는 시스템 함수를 이용하면 간접적 조작은 가능하다.

  • 프로세스의 우선순위 변경
BOOL SetPriorityClass ( 
	HANDLE hProcess // 우선순위를 변경할 프로세스의 핸들을 전달한다.
    DWORD dwPriorityClass // 새롭게 적용할 우선순위 정보를 전달한다.
    }
// hProcess가 가리키는 프로세스의 우선순위를 dwPriorityClass로 변경시킨다.

핸들 : 핸들이란 커널 오브젝트에 할당되는 숫자에 지나지 않는다

"중요하기 때문에 이름을 부여헸을 뿐이지 단순히 커널오브젝트는 구조체 변수이고 핸들은 숫자이다"

  • 커널 오브젝트에 할당되느 숫자! 핸들
    특정 프로세스의 우선순위를 높인다 -> 우선순위 정보를 변경해줘야한다 -> 우선순위 정보는 프로세스 커널 오브젝트에 존재한다 ->핸들을 통해 커널 오브젝트를 지시한다.

  • 핸들 정보는 어디서?
    핸들 정보를 얻는 방법은 커널 오브젝트의 종류에 따라서 다양하다

GetCurrenProcess() 현재 실행되고 있는 프로세스의 핸들을 얻어옴(이 함수를 호출한 프로세스의 핸들을 얻어온다)

-Busy Wating
for(DWORD i = 0; i < 10000; i++)
for(DWORD i = 0; i < 10000; i++)
cpu의 연산이 매우 빠르기 때문에 결과를 눈으로 관찰하기위해 실행을 늦출 필요가있음

위 반복문이 아닌 Sleep이라는 함수도있지만 Sleep함수는 자신의 프로세스의 상태를 블록 상태로 변경시킴 이는 원하는 결과를 관찰할수없음.

그렇기때문에 단순 반복문으로 러닝상태에 머무를수있는 비지웨이팅을 구현

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

  • 커널 오브젝트의 종속 관계

"커널 오브젝트는 Windows 운영체제에 종속적이다"

-예제-

프로세스의 A가 생성되면 프로세스 A의 커널오브젝트와 "프로세스 A의 핸들 테이블"이 생성됨

핸들 테이블에는 프로세스A 의 커널 오브젝트의 핸들값과 대상이 기록됨(이를통해 커널 오브젝트에 접근) + Usage Count는 자기자신 1 + 운영체제 1 = 2

프로세스 A가 프로세스 B를 생성하면 프로세스 B의 커널 오브젝트가 생성되고 프로세스 B의 핸들테이블에 해당 커널 오브젝트를 가리키는 핸들이 기록됨 + Usage Count는 자기자신과 부모 A , 즉 2
그리고 프로세스 A의 핸들 테이블에 B의 핸들이 기록됨

즉 커널오브젝트에 핸들이랑 값이 매핑되어있는 것이 아닌 프로세스에 정하는 값이다(종속정이다)
각 핸들테이블의 값이 같다고해서 같은 핸들은 아니다.

그리고 프로세스 B가  파일을 연다면 파일의 커널오브젝트 생성 + 프로세스 B의 핸들테이블에 해당 커널 오브젝트의 핸들이 기록 파일의 UC는 1 프로세스 B에서만 접근

프로세스 B가 종료되면 B의 핸들 테이블이 없어지고 B의 커널오브젝트는 바로 삭제되는것이 아닌 UC를 보고 0이라면삭제

프로세스가 종료될때 (return,exit) 해당 종료코드를 프로세스의 커널 오브젝트에 기록 만약 이 코드가 커널 오브젝트에 저장되지 않으면 이 종료코드를 확인할 방도가 없음

자기자신의 핸들정보는 핸들테이블에 기록되지않는다.
자기자신의 핸들값은 -1의 상수값으로 약속됨


  1. 커널 오브젝트는 프로세스에 종속적인 것이 아니라, 운영체제 종속적인 관계로 커널 오브젝트의 소멸시점은 운영체제에 의해서 결정된다.

  2. 커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영체제에 종속적인 관계로 여러 프로세스에 의해서 접근 가능하다.

  • 핸들의 종속 관계

핸들은 운영체제에 종속적이지 않고 프로세스에 종속적이다

  • 예제를 통한 종속 관계의 이해

커널 오브젝트의 공유 예제
"A 프로세스가 B 프로세스를 생성한다. 그러자 B 프로세스는 자신의 우선순위를 높인다. 잠시 후 열받은 A프로세느는 B프로세스의 우선순위를 원래대로 돌려 놓는다.

-PROCESS_INFORMATION 구조체

운영체제는 프로세스를 생성할 때마다 프로세스들을 구분짓기 위한 ID를 할당한다. 위 구조체의 세 번째 멤버 dwProcessID는 새로 생성되는 프로세스 ID정보로 채워 지게된다.

프로세스 핸들과 프로세스 ID의 차이점
"프로세스 핸들은 프로세스의 커널 오브젝트를 가리키기 위한 것이고 , 프로세스 ID 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위한 것이다.

윈도우즈 운영체제는 프로세스를 생성하면, 프로세스 내부적으로 쓰레드라는 개념의 가벼운 프로세스를 생성해서 이를 통해 main 함수가 호출 되게끔 디자인되어 있다.

CreateProcess 함수를 통해서 프로세스를 생성하면 쓰레드라는 시스템 리소스도 더불어 생성됨. 바로 이 쓸레드의 핸들과,ID 정보가 각각 두 번째 , 네 번쨰 멤버에 채워지게 된다.

커널 오브젝트와 Usage Count

"커널 오브젝트는 프로세스에 종속적인 것이 아니라, 운영체제에 종속적인 관계로 커널 오브젝트 소멸시기는 운영체제에 의해서 결정된다."

-ClostHandle 함수에 대한 정확한 이해

A라는 이름의 프로세스가 생성되면, A프로세스를 위한 커널 오브젝트가 생성된다. 이때 커널 오브젝트는 완전히 프로세스를 대표하게된다.

하지만 프로세스가 소멸된다고 해서 커널 오브젝트가 소멸된다고 말할 수 없다.
소멸되고 안되고는 운영체제가 결정할일임

-CloseHandle 함수와 프로세스 소멸

BOOL CloseHandle (
HANDLE hObject
);

"이 핸들이 가리키는 리소스가 더 이상 필요치 않으니, 이에 해당하는 리소스를 해제 하고 커널 오브젝트도 소멸시켜라!"

GetExitCodeProcess(pi.hProcess, state);
이 함수는 첫 번째 인자로 전달된 핸들이 가리키는 프로세스가 반환하는 종료코드(Exit Code, 종료 상태를 알리는 값)를 얻기 위해 사용되는 함수이다. 만약에 전달된 핸들에 해당하는 프로세스가 종료되지 않고 실행 중이라면,STILL_ACTIVE를 대신해서 반환한다.

  • 커널 오브젝트와 Usage Count

" 자식 프로세스의 종료코드는 자식프로세스의 커널 오브젝트에 저장된다"
자식 프로세스가 종료될 때 커널 오브젝트도 동시에 소멸된다면 부모 프로세스는 종료코드를 얻을수 없게 된다. 때문에 프로세스가 종료되었다고 해서 커널 오브젝트까지 동시에 소멸시키지는 않는다.

그렇다면 언제 커널 오브젝트를 소멸시키는 것이 좋겠는가? 해당 "커널 오브젝트를 참조히는 대상이 하나도 없을 때 소멸시키는 것이 가장 이상적이고, 이것이 Windows가 커널 오브젝트를 소멸시기를 결정하는 방식이다."

Windows는 이러한 정책을 기반으로 커널 오브젝트 소멸시기를 결정짓기 위해 Usage Count(참조 횟수)라는 것을 관리하다. 이 Usage Count가 0이 되는 순간, 해당 커널 오브젝트는 소멸된다.
다음 그림은 위 예제에서 자식 프로세스 생성 이후 상황을 보여준다.

프로세스는 생성과 동시에 커널 오브젝트의 Usage Count가 1이된다. 만약 생성과 동시에 UsageCount가 0으로초기화된다면, 커널 오브젝트 소멸 원칙에 의해 생성과 동시에 소멸되고 만다.이를 막기 위해서 자식 프로세스는 생성과 동시에 Usage Count가 1이된다.

이렇게 초기화된 이후부터는 커널 오브젝트에 접근 가능한 대상이 늘어날 때마다 Usage Count가 하나씩 증가한다. 접근 가능 대상이 늘어난다는 것은 커널 오브젝트에 접근 가능한 핸들 갯수의 증가를 의미한다.

그렇다면 자식 프로세스의 Usage Count는 1이 아니라 2가 되어야 한다. 왜냐하면 부모 프로세스가 CreateProcess 함수 호출 과정에서 자식 프로세스의 핸들을 얻기 때문이다(PROCESS_INFORMATION 구조체를 통해서). 실제로 자식 프로세스 생성이 완료되고 나면 Usage Count는 2가된다.

Usage Count는 해당 커널 오브젝트의 멤버로 존재한다.

-Usage Count와 CloseHandle

CloseHandle 함수는 핸들을 반환하면서 커널 오브젝트의 Usage Count를 하나 감소시키는 기능을 지닌다.
프로세스(쓰레드도 마찬가지)의 경우에는 프로세스가 종료되는 시점에서도 Usage Count가 하나 감소한다는 사실이다.

이것만은 알고 갑시다.

  1. 커널 오브젝트와 핸들

    커널 오브젝트는 커널에서 관리하는 중요한 정보들을 담아둔 데이터 블록이고,
    핸들이란 커널 오브젝트를 가리키기 위한 커널 오브젝트에 부여되는 숫자이다.

  2. 운영체제에 종속적인 커널 오브젝트

    커널 오브젝트의 관리를 프로세스가 하는것이아닌 운영체제가 한다는 의미 소멸시점을 운영체제가 정하고 여러프로세스에서 접근할수있음

  3. Usage Count

프로세스의 접근 가능한 대상의 수를 기록해둔것
접근 가능한 대상의수가 늘어날때마다 1씩증가하고 closeHandle을 통해 핸들을 반환하면 하나 감소시킴

  1. CloseHandle 함수

CloseHandle 함수는 핸들을 반환하면서 커널 오브젝트의 Usage Count를 하나 감소시키는 기능을 지닌다.
5. 프로세스의 Usage Count

자신의 소멸막기위해 usage count는 1로 초기화되고 부모프로세스가 자식프로세스의 핸들을 얻기때문에 2로 증가한다.

  1. 종료코드

return exit를 통해 반환값을 반환하고 종료를 할수있다.

  1. 부모 프로세스가 자식 프로세스 핸들을 곧바로 반환하는 이유

자식프로세스가 종료되었을때 핸들을 바로반환해놓지않으면 usage count가 1이기때문에 커널오브젝트가 반환되지않음 자식 프로세스의 핸들을 통해 종료코드를 얻을려면 반환시간을 늦춰야만함

0개의 댓글