가상 머신 생성 식별자

Windows 8 및 Windows Server 2012 가상 머신에서 실행되는 소프트웨어가 시간 이동 이벤트가 발생했을 수 있음을 감지하는 기능을 도입합니다. 시간 이동 이벤트는 가상 머신 스냅샷의 애플리케이션 또는 가상 머신 백업 복원의 결과로 발생할 수 있습니다. 이 기능에 대한 자세한 내용은 Virtual Machine Generation 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 SP1(서비스 팩 1)
    • 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, Windows Server 2008 SP2 이상 운영 체제를 사용하는 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;
};

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

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