6장. 커널 오브젝트와 오브젝트 핸들

유니야·2023년 1월 6일

✏️ 커널 오브젝트

커널에 의해 관리되는 리소스 정보를 담고 있는 데이터 블록. 구조체 변수.
즉 윈도우즈에 의해 생성되는 모든 리소스는 커널 오브젝트 생성을 동반한다.

  • 커널(Kernel)
    운영체제의 핵심이 되는 부분.

리소스: 운영체제에 의해 생성 및 소멸되는 것.
ex) 배열을 선언했을 경우, 배열은 프로그래머에 의해 관리되는 리소스 정보를 담고 있는 데이터 블록.
ex) 파일 - 운영체제 + 프로그래머에 의해 관리되는 리소스. 파일 자체는 읽고 쓰는 권한을 가지고 있지 않고, 운영체제가 이를 관리한다. 즉, 프로그래머가 파일 관련 함수를 사용해 파일 할당을 요청하면 운영체제는 요구에 맞게 파일을 생성. 그리고 합법적인 접근을 하지 않았을 경우에도 운영체제가 이를 막는다.

리소스마다 관리되어야 하는 데이터가 다르기 때문에 각 커널 오브젝트는 다르게 디자인되어 있다.

ex) 프로세스 생성 - 해당 프로세스를 관리하기 위한 커널 오브젝트가 생성됨

  • OS가 알아야할 정보
    • 프로세스의 상태 정보
      스케줄러에 의해 실행 가능한 상태로 둘 것인지, 결정할 수 있다.
    • 우선 순위

ex) 파일 생성 - 해당 파일을 관리하기 위한 커널 오브젝트가 생성됨

  • OS가 알아야할 정보
    • 파일에 대한 접근 정보
      ReadMode로 열었을 때 WriteMode로 접근할 경우 OS는 이를 막아야 한다.
    • 어디까지 읽었는지 정보
      Read요청을 할 때, 파일을 어디까지 읽었는지 정보를 알고 있어야 순차적으로 읽을 수 있다.
  • 프로세스 기반 커널 오브젝트 생성 과정


    1) 프로그래머는 여러 정보와 함께 함수 호출 통해 프로세스 생성 요청
    2) 해당 정보는 윈도우즈 운영 체제에 전달됨
    3) 해당 정보를 기반으로 프로세스 생성, 동시에 커널 오브젝트 생성

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

핸들 (Handle)
커널 오브젝트를 간접적으로 관리하기 위한 고유 번호.
프로세스 생성 - 프로세스의 커널 오브젝트 생성 - 핸들 생성

커널 오브젝트는 우선 순위 정보를 가지고 있음.
커널 오브젝트의 경우 매우 중요하기 때문에 우선 순위 정보는 OS에 의해 관리되고, 프로그래머가 직접적으로 접근할 수 없다.
따라서 프로그래머가 해당 우선 순위를 변경하고 싶을 경우, 윈도우즈에서 제공되는 함수를 이용해 간접적으로 접근해야 한다.
우선 순위를 변경하기 위해서는 어떤 프로세스의 우선 순위를 바꿀지 정보를 넘겨야 하는데, 이 때 핸들이 사용된다. 핸들을 얻는 방법은 리소스마다 다르다.

  • 프로세스의 우선 순위 변경
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, TCHAR* argv[])
{
    STARTUPINFO si = {0,};
    PROCESS_INFOMATION pi;

    si.cb = sizeof(si);
    TCHAR command[] = _T("Operation2.exe");

    bool bState = CreateProcess(
        NULL, command, NULL, NULL,
        TRUE, 0, NULL, NULL, &si, &pi
    );

    if (false != bState)
    {
        while (true)
        {
            for (DWORD i = 0; i < 10000; ++i)
            {
                for (DWORD i = 0; i < 10000; ++i)
                {
                    // Busy Waiting!
                }
            }
            _fputts(_T("Operation1.exe\n"), stdout);
        }
    }

    return 0;
}
  • Busy Waiting
    CPU는 고속 연산 장치이기 때문에 결과 출력을 눈으로 보기 어렵다. 이 경우 sleep()함수 호출을 통해 프로세스를 잠시 Blocked 상태로 둘 수 있다. 그러나 우선 순위가 높은 애가 등장하면 우선 순위에게 자신의 순서를 넘기고 싶은 경우, 이중 for문 등으로 Busy Waiting을 해서 자신을 잠시 Ready 상태로 두는 방법이 있다. 이렇게 하면 우선 순위가 높은 프로세스가 등장할 때마다 서로 실행권을 넘겨주게 된다.
    • sleep(): 프로세스가 자신을 N초동안 Blocked 상태로 두는 것.

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

💻 프로세스 생성 과정을 통한 커널 오브젝트와 핸들의 관계 설명

해당 모든 과정은 운영체제에서 일어나는 일.
프로세스 A가 B를 생성했을 경우, B는 파일을 생성하는 상황.

1) 프로세스의 A의 생성과 함꼐, 프로세스 A의 커널 오브젝트와 + 핸들 테이블이 생성됨
해당 핸들 테이블은 프로세스 A가 접근할 수 있다.
핸들 테이블에는 커널 오브젝트의 핸들 값이 저장되어 있고, 이를 key값으로 매핑되는 자신의 커널 오브젝트를 찾을 수 있다. ex) 핸들 값 = 3

  • Usage Count: 프로세스 A에 접근할 수 있는 사용자.
  • UC -> 1.
    GetCurrentProcess()를 통해 얻게되는 상수 값을 통해 자기 자신에게 접근할 수 있다 = 1

2) 프로세스 A를 주체로 프로세스 B를 생성
프로세스 B의 핸들 값은 핸들 테이블에 등록되고, 이는 프로세스 B의 커널 오브젝트를 가리킴. ex) 핸들 값 = 3

  • UC -> 2
    나 자신의 고유 핸들 값에 의한 접근 + 부모인 프로세스 A의 핸들 값에 의한 접근
    프로세스 B의 핸들 값이 A에게 반환됨. 즉 프로세스 A는 B의 커널 오브젝트에 접근할 수 있는 핸들 값을 얻게 된다. 그러나 이 핸들 값은 프로세스 A 자신의 핸들 값과 중복되는 3으로 둬서는 안 된다. 그리고 해당 핸들 값은 프로세스 B의 핸들 값과 별개로 프로세스 A의 핸들 테이블에 등록된다.
    즉, 핸들 값은 커널 오브젝트가 아닌 프로세스에 종속되어 있고, 따라서 각 프로세스의 핸들 테이블 영역에서만 의미를 지닌다.

  • UC를 두는 이유

    프로세스 B가 사라질 경우, B의 핸들 테이블도 사라진다. 그러나 프로세스 B의 커널 오브젝트에 아직 관심을 가지고 있는 프로세스가 있을 수 있기 때문에(여기서는 프로세스 A) 커널 오브젝트까지 바로 사라지는 것은 아니다. 이 경우 커널 오브젝트의 UC는 1이 깎이고, UC가 0일 때 커널 오브젝트는 종료된다. 이 시점은 운영체제에 의해 결정된다.
    그러나 파일의 경우 UC = 0이 되었다고 물리적으로 사라지는 것은 아니다.

  • 프로세스 자신의 핸들 정보는 핸들 테이블에 등록되지 않는다. 그러면 프로세스는 자신의 커널 오브젝트에 어떻게 접근하는가.

    GetCurrentProcess()함수를 호출하면 임의의 핸들 값을 반환한다. 해당 값은 어떤 프로세스가 호출하던 언제나 같은 고유 값이다. 프로세스 자기 자신을 의미하는 상수 값. 이 값을 통해 자기 자신의 커널 오브젝트에게 접근할 수 있다.

  • 커널 오브젝트를 생성한 주체가 종료되었는데도 불구하고 커널 오브젝트를 남겨둬야 하는 이유

    부모 프로세스와 자식 프로세스는 기본적으로 서로 독립적이지만, 부모 프로세스가 자식 프로세스의 실행 상태에 따라 자신의 상태를 결정지을 수도 있다.
    프로세스 B가 종료될 경우, 종료 코드에서 반환한 값이 B의 커널 오브젝트에 저장된다. 따라서 프로세스 B의 종료와 함께 커널 오브젝트가 소멸된다면 해당 반환 값을 부모 프로세스 A가 확인할 수 없기 때문에 자식 프로세스가 정상적으로 종료되었는지 알 수 없게 된다.

  • 파일에 접근하기 위한 방법
    1) ANSI 표준 함수
    내부적으로 윈도우즈의 System 함수를 호출하는 구조.
    2) 윈도우즈에서 제공하는 System 함수 이용

  • CloseHandle() 함수
    커널 오브젝트의 UC를 1 줄이고, 핸들 테이블에 등록되어 있는 핸들 정보를 삭제하는 함수.
    자식 프로세스 B의 커널 오브젝트의 UC가 1일 경우, 이는 부모 프로세스 A가 종료되어야 비로소 UC가 0이 되고, 종료된다. 그러나 프로세스 B의 커널 오브젝트가 더 이상 필요가 없는데도 남아 있을 경우 이는 리소스 낭비이다. 따라서 어떤 커널 오브젝트가 더 이상 필요없을 경우 CloseHandle()함수를 통해 이를 명시해줘야 한다.

0개의 댓글