ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2015.01.22 커널 오브젝트 Usage Count
    Legacy/뇌자극 - WinSysProg 2015. 1. 21. 09:56
    728x90

    커널 오브젝트와 Usage Count

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

    보통 커널 오브젝트를 생성한 주체가 커널 오브젝트를 소멸시킬 권한(소멸시킬 시점을 결정지을 권한)을 가지고 있다고 생각한다. 이는 맞는 말이지만, 커널 오브젝트를 생성한 주체가 누구냐는 오해할 소지가 있다. 커널 오브젝트를 생성한 주체는 CreateProcess 함수를 호출하는 프로그래머가 아니라, 이 함수의 호출을 받은 운영제체가 커널 오브젝트 생성의 주체이다.


    CloseHandle 함수에 대한 정확한 이해

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

    "프로세스가 소멸된다고 해서 커널 오브젝트가 소멸된다고 말 할 수 없다."

    소멸될 수도 있지만, 소멸되지 않을 수도 있다. 이것은 운영체제가 결정할 일이다. 그렇다면 운영체제가 커널 오브젝트의 소멸시기를 결정하는 기준은 무엇일까? 이와 관련해서 CloseHandle 함수를 알아야 한다.

    KernelObjProb1.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #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;
    }
     
     
    cs


    KernelObjProb2.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #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 < 10000; i++)
                for(DWORD i = 0; i < 10000; i++);
            _fputts(_T("KernelObjProb2.exe \n"), stdout);
            n++;
        }
     
        return 0;
    }
     
    cs


    CloseHandle은 함수 이름처럼 핸들을 닫는 기능을 지니고 있다. 

    BOOL CloseHandle(

    HANDLE hObject

    );

    If, the function succeeds, the return value is nonzero.


    위의 예제를 실행해보면 핸들을 닫는다고 해서 프로세스가 종료되지 않는 다는 것을 확인할 수 있다.


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

    OperationStateParent.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #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;
        si.lpTitle = _T("return & exit");
     
        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++);
     
        GetExitCodeProcess(pi.hProcess, &state);
        if(state == STILL_ACTIVE)
            _tprintf(_T("STILL_ACTIVE \n\n"));
        else
            _tprintf(_T("state : %d \n\n"), state);
     
        return 0;
    }
     
    cs

    OperationStateChild.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <stdio.h>
    #include <stdlib.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);
        }
     
        _tprintf(_T("Operation Result : %f \n"), num1/num2);
     
        return 1;
    }
    cs


    커널 오브젝트와 Usage Count

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

    그렇다면 언제 커널 오브젝트를 소멸시키는 것이 좋겠는가? 해당 커널 오브젝트가 참조하는 대상이 하나도 없을 때 소멸시키는 것이 가장 이상적이고, 이것이 Windows가 커널 오브젝트 소멸시기를 결정하는 방식이다. 바꿔 말한다면 커널 오브젝트를 참조하는 프로세스가 하나라도 있을 시에는 커널 오브젝트는 소멸되지 않는다.

    Windows는 이러한 정책을 기반으로 커널 오브젝트 소멸시기를 결정짓기 위해 Usage Count(참조 횟수)라는 것을 관리한다. 이 Usage Count가 0이 되는 순간, 해당 커널 오브젝트는 소멸된다.

    프로세스는 생성과 동시에 커널 오브젝트의 Usage Count가 1이 된다. 만약 생성과 동시에 Usage Count가 0으로 초기화된다면, 커널 오브젝트 소멸 원칙에 의해 생성과 동시에 소멸되고 만다. 이를 막기 위해서는 자식 프로세스는 생성과 동시에 Usage Count가 1이 된다. 이렇게 초기화된 이후부터는 Usage Count가 하나씩 증가한다. 접근 가능 대상이 늘어난다는 것은 커널 오브젝트에 접근 가능한 핸들 개수의 증가를 의미한다.

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


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


    프로세스 강제종료

    CloseHandle 함수는 프로세스 종료와 별개의 문제이다. 간혹 CloseHandle 함수를 프로세스 종료 함수로 오해하는 경우도 있음. 프로세스 종료 요청시에 사용되는 함수는 TerminateProcess 함수이다. 문제는 강제 종료 요청이라는 점이다. 가능하면 사용하지 않는 것이 좋음.


    자식 프로세스를 생성하자 마자 CloseHandle 함수를 호출해야 정상적으로 자식 프로세스의 커널 오브젝트를 소멸시킬 수 있음.

    ※ 자식 프로세스 생성과 동시에 핸들을 반환하는 것이 일반적이기는 하나, 핸들의 반환 시점을 뒤로 미뤄야만 하는 경우도 있다.

    ex) 종료코드를 얻기 위해서는 CloseHandle 함수의 호출을 뒤로 미룰 수 밖에 없다.



Designed by Tistory.