가상 머신 생성 식별자

Windows 8 및 Windows Server 2012 가상 머신에서 실행 중인 소프트웨어가 시간 이동 이벤트가 발생했을 수 있음을 감지하는 기능을 도입합니다. 시간 이동 이벤트는 가상 머신 스냅샷 애플리케이션 또는 가상 머신 백업 복원의 결과로 발생할 수 있습니다. 이 기능에 대한 자세한 내용은 Virtual Machine 생성 ID 백서를 참조하세요.

사전 요구 사항

가상 머신 내부에서 가상 머신 생성 식별자를 사용하려면 가상 머신이 다음을 준수해야 합니다.

  • 가상 머신 생성 식별자에 대한 지원을 구현하는 하이퍼바이저에서 가상 머신을 실행해야 합니다. 현재 다음과 같습니다.

    • Windows 8
    • Windows Server 2012
    • Microsoft Hyper-V Server 2012
  • 가상 머신은 가상 머신 생성 식별자를 지원하는 게스트 운영 체제를 실행해야 합니다. 현재는 다음과 같습니다.

    다음 운영 체제는 가상 머신 생성 식별자를 기본적으로 지원합니다.

    • Windows 8
    • Windows Server 2012

    Windows 8 또는 Windows Server 2012 Hyper-V 통합 서비스가 설치된 경우 다음 운영 체제를 게스트 운영 체제로 사용할 수 있습니다.

    • Windows Server 2008 R2 SP1(서비스 팩 1)
    • Windows 7 + 서비스 팩 1(SP1)
    • Windows Server 2008 서비스 팩 2(SP2)
    • Windows Server 2003 R2
    • Windows Server 2003 SP2(서비스 팩 2)
    • Windows Vista SP2(서비스 팩 2)
    • Windows XP SP3(서비스 팩 3)

가상 머신 생성 식별자 가져오기

프로그래밍 방식으로 가상 머신 생성 식별자를 가져오려면 다음 단계를 수행합니다.

참고

제대로 작동하려면 관리자 또는 시스템 권한으로 다음을 실행해야 합니다.

 

  1. 앱에 헤더 파일 "vmgenerationcounter.h"를 포함합니다. 헤더 파일에는 다음 정의가 포함되어 있습니다.

    DEFINE_GUID(
        GUID_DEVINTERFACE_VM_GENCOUNTER,
        0x3ff2c92b, 
        0x6598, 
        0x4e60, 
        0x8e, 
        0x1c, 
        0x0c, 
        0xcf, 
        0x49, 
        0x27, 
        0xe3, 
        0x19);
    
    #define VM_GENCOUNTER_SYMBOLIC_LINK_NAME L"VmGenerationCounter"
    
    #define IOCTL_VMGENCOUNTER_READ CTL_CODE( \
        FILE_DEVICE_ACPI, \
        0x1, METHOD_BUFFERED, \
        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
    
    typedef struct _VM_GENCOUNTER
    {
        ULONGLONG GenerationCount;
        ULONGLONG GenerationCountHigh;
    } VM_GENCOUNTER, *PVM_GENCOUNTER;
    
  2. CreateFile 함수를 사용하여 "\\.\VmGenerationCounter" 디바이스에 대한 핸들을 엽니다. 또는 PnP 관리자를 사용하여 디바이스 인터페이스 GUID_DEVINTERFACE_VM_GENCOUNTER 사용할 수 있습니다({3ff2c92b-6598-4e60-8e1c-0ccf4927e319}). 앱이 가상 머신에서 실행되고 있지 않으면 이러한 개체가 존재하지 않습니다.

  3. IOCTL_VMGENCOUNTER_READ IOCTL을 드라이버에 보내 생성 식별자를 검색합니다.

    IOCTL_VMGENCOUNTER_READ IOCTL은 폴링이벤트 구동의 두 가지 모드 중 하나로 작동합니다.

    폴링 모드에서 IOCTL을 실행하려면 입력 버퍼 길이가 0인 IOCTL을 제출합니다. 이에 대한 응답으로 드라이버는 현재 세대 식별자를 검색하고 출력 버퍼에 쓰고 IOCTL을 완료합니다.

    이벤트 기반 모드에서 IOCTL을 발급하려면 기존 세대 식별자가 포함된 입력 버퍼를 사용하여 IOCTL을 제출합니다. 이에 대한 응답으로 드라이버는 현재 세대 식별자가 전달된 세대 식별자와 다를 때까지 기다립니다. 세대 식별자가 변경되면 드라이버는 현재 세대 식별자를 출력 버퍼에 쓰고 IOCTL을 완료합니다.

    두 모드 모두에서 출력 버퍼의 형식과 길이는 VM_GENCOUNTER 구조체에 의해 결정됩니다.

    폴링 모드는 위에 나열된 모든 게스트 운영 체제에서 지원됩니다. 이벤트 기반 모드는 WINDOWS Vista SP2, WINDOWS Server 2008 SP2 이상 운영 체제에서만 지원됩니다. 이전 운영 체제에서 IOCTL은 이벤트 기반 모드에서 실행될 때 오류 코드 ERROR_NOT_SUPPORTED 실패합니다.

    세대 식별자는 드라이버에서 검색하는 시간과 IOCTL이 완료된 시간 사이에 변경될 수 있습니다. 이로 인해 클라이언트 앱이 부실 데이터를 수신할 수 있습니다. 이를 방지하기 위해 클라이언트 앱은 이벤트 기반 모드를 사용하여 결국 세대 식별자에 대한 업데이트에 대해 학습하도록 할 수 있습니다. 클라이언트 앱의 현재 식별자를 입력으로 사용하여 이벤트 기반 모드는 호출자가 알림을 놓칠 수 있는 잠재적인 경합 조건을 방지합니다.

다음 코드 예제에서는 위의 작업을 수행하여 가상 머신 생성 식별자를 가져오는 방법을 보여 줍니다.

참고

제대로 작동하려면 관리자 또는 시스템 권한으로 다음 코드를 실행해야 합니다.

 

HRESULT GetVmCounter(bool fWaitForChange)
{
    BOOL success = FALSE;
    DWORD error = ERROR_SUCCESS;
    VM_GENCOUNTER vmCounterOutput = {0};
    DWORD size = 0;
    HANDLE handle = INVALID_HANDLE_VALUE;
    HRESULT hr = S_OK;

    handle = CreateFile(
        L"\\\\.\\" VM_GENCOUNTER_SYMBOLIC_LINK_NAME,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (handle == INVALID_HANDLE_VALUE)
    {
        error = GetLastError();

        wprintf(
            L"Unable to open device %s. Error code = %d.", 
            VM_GENCOUNTER_SYMBOLIC_LINK_NAME, 
            error);

        hr = HRESULT_FROM_WIN32(error);

        goto Cleanup;
    }

    /*
    Call into the driver. 

    Because the 4th parameter to DeviceIoControl (nInBufferSize) is zero, this 
    is a polling request rather than an event-driven request.
    */
    success = DeviceIoControl(
        handle,
        IOCTL_VMGENCOUNTER_READ,
        NULL,
        0,
        &vmCounterOutput,
        sizeof(vmCounterOutput),
        &size,
        NULL);

    if (!success)
    {
        error = GetLastError();

        wprintf(L"Call IOCTL_VMGENCOUNTER_READ failed with %d.", error);

        hr = HRESULT_FROM_WIN32(error);

        goto Cleanup;
    }

    wprintf(
        L"VmCounterValue: %I64x:%I64x",
        vmCounterOutput.GenerationCount,
        vmCounterOutput.GenerationCountHigh);

    if (fWaitForChange)
    {
        /*
        Call into the driver again in event-driven mode. DeviceIoControl won't 
        return until the generation identifier has changed.
        */
        success = DeviceIoControl(
            handle,
            IOCTL_VMGENCOUNTER_READ,
            &vmCounterOutput,
            sizeof(vmCounterOutput),
            &vmCounterOutput,
            sizeof(vmCounterOutput),
            &size,
            NULL);

        if (!success)
        {
            error = GetLastError();

            wprintf(L"Call IOCTL_VMGENCOUNTER_READ failed with %d.", error);

            hr = HRESULT_FROM_WIN32(error);

            goto Cleanup;
        }

        wprintf(
            L"VmCounterValue changed to: %I64x:%I64x",
            vmCounterOutput.GenerationCount,
            vmCounterOutput.GenerationCountHigh);
    }

Cleanup:

    if (handle != INVALID_HANDLE_VALUE)
    {
        CloseHandle(handle);
    }

    return hr;
};

시간 이동 이벤트가 발생했는지 확인

가상 머신 생성 식별자를 가져온 후에는 나중에 사용할 수 있는 ID를 저장해야 합니다. 앱이 데이터베이스에 커밋하는 등 시간에 민감한 작업을 수행하기 전에 생성 식별자를 다시 가져와 저장된 값과 비교해야 합니다. 식별자가 변경된 경우 이는 시간 이동 이벤트가 발생했으며 앱이 적절하게 작동해야 했음을 의미합니다.