가상 머신 생성 식별자

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)
    • sp1(서비스 팩 1)을 사용하여 Windows 7
    • 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 구조에 의해 결정됩니다.

    폴링 모드는 위에 나열된 모든 게스트 운영 체제에서 지원됩니다. 이벤트 구동 모드는 SP2, SP2를 사용한 Windows Server 2008 및 이후 운영 체제가 있는 Windows Vista에서만 지원됩니다. 이전 운영 체제에서는 이벤트 구동 모드에서 실행될 때 오류 코드 ERROR_NOT_SUPPORTED IOCTL이 실패합니다.

    생성 식별자는 드라이버에서 검색하는 시간과 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;
};

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

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