Veri Değişimi: Hyper-V'da konak ve konuk arasında bilgi paylaşmak için anahtar-değer çiftlerini kullanma

Veri Değişimi, bir sanal makine (konuk) ile Hyper-V konağı arasında küçük bilgi parçalarını paylaşmak için kullanılabilen bir tümleştirme hizmetidir (anahtar-değer çifti değişimi veya KVP olarak da bilinir). Sanal makine ve konakla ilgili genel bilgiler otomatik olarak oluşturulup anahtar-değer çiftleri olarak depolanır. Özel verileri paylaşmak için kendi çiftlerinizi de oluşturabilirsiniz.

Anahtar-değer çiftleri bir "anahtar" ve "değer"den oluşur. Her ikisi de dizedir; başka hiçbir veri türü desteklenmez. Anahtar-değer çifti oluşturulduğunda veya değiştirildiğinde, hem konuk hem de konak tarafından görünür hale gelir. KVP verileri Hyper-V VMbus üzerinden hareket eder ve konuk ile konak arasında herhangi bir ağ bağlantısı gerektirmez.

Anahtar-değer çiftleri oluşturulduktan sonra silinene kadar kalır. Anahtar-değer çiftleri oluşturan tüm uygulamalar artık gerekli olmadığında bunları silmelidir. Anahtar-değer çiftleri, Dinamik Geçiş sırasında sanal makineyle birlikte hareket eder.

Windows konukları

Windows konuklarında, KVP verileri kayıt defterinde aşağıdakiler altında depolanır:

HKLM\SOFTWARE\Microsoft\Virtual Machine

Veriler şu alt anahtarlarda düzenlenmiştir:

  • Virtual Machine\Auto – Konuğu açıklayan veriler. Tümleştirme hizmeti sürücüleri yüklendikten sonra oluşturulur. Konağa içsel veri olarak görünür.
  • Sanal Makine\Dış – Bir kullanıcı tarafından konaktan konuğa gönderilen veriler.
  • Virtual Machine\Guest – Konukta oluşturulan veriler. Ana bilgisayara dışsal veri olarak görünür.
  • Virtual Machine\Guest\Parameter – Konağı tanımlayan veriler, konaktan konuğa iletilir.

Misafir içinden değer eklemek, HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest altında yeni bir dize değeri oluşturmak kadar basittir. Bu konumu değiştirmek için konukta yönetici olmanız gerekir. Değeri almak için ana makineden veya kullanıcı izinlerine sahipseniz uzak bir makineden WMI (PowerShell veya başka araçlar) kullanabilirsiniz.

Kayıt defteri boyutu sınırları hakkında bilgi için Kayıt Defteri Öğesi Boyut Sınırları (eski) makalesine bakın.

Konuğa yeni bir anahtar-değer çifti ekleyin.

Bu örnekte , Durum değeri olarak Readyayarlanır:

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest"
Set-ItemProperty -Path $regPath -Name "Status" -Value "Ready" -Type String

Değeri değiştirmek için aynı söz dizimini kullanabilirsiniz.

Konuktaki anahtar-değer çiftlerini sorgula

External alt anahtarının değerini sorgulamak için (konaktan konuk için gönderilen veriler):

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\External"
Get-ItemProperty -Path $regPath -Name "Name"

Linux konukları

Linux'un kayıt defteri yoktur, bu nedenle KVP öğeleri dosya sisteminde depolanır. bir daemon işlemi, hv_kvp_daemonişlemeyi işlemek için çalışıyor olmalıdır. Linux Integration Services (LIS) veya çekirdek içi sürücülerin yüklü olduğu çoğu dağıtım için bu daemon otomatik olarak başlatılır. Bazı durumlarda, daemon'u yüklemek ve başlatmak için ek adımlar gerekebilir.

Linux tümleştirme hizmetleri, KVP havuzlarıyla veri değişimi uygular. KVP havuzu, belirli bir yolda depolanan bir dosyadır. Dört havuz dosyası vardır:

/var/lib/hyperv/.kvp_pool_0
/var/lib/hyperv/.kvp_pool_1
/var/lib/hyperv/.kvp_pool_2
/var/lib/hyperv/.kvp_pool_3

Bu havuz dosyaları aşağıdaki Windows kayıt defteri anahtar kümelerine eşleniyor:

  • Havuz 0: Virtual Machine\External
  • Havuz 1: Virtual Machine\Guest
  • Havuz 2: Virtual Machine\Auto
  • Havuz 3: Virtual Machine\Guest\Parameter

Uyarı

Linux KVP desteği hakkında daha fazla bilgi için bkz. Hyper-V üzerinde Linux ve FreeBSD Sanal Makineleri.

Anahtar-değer çifti altyapısı, Linux yazılım güncelleştirmesi olmadan düzgün çalışmayabilir. Sorunlarla karşılaşırsanız güncelleştirme için dağıtım satıcınıza başvurun.

Havuz yapısı

Her havuz dosyası şu yapıya sahip kayıtlar içerir:

struct kvp_record
{
    unsigned char key[ HV_KVP_EXCHANGE_MAK_KEY_SIZE ];
    unsigned char value[ HV_KVP_EXCHANGE_MAX_VALUE_SIZE ];
};

Bu boyut sabitleri içinde hyperv.h tanımlanır (Linux çekirdek kaynaklarıyla dağıtılmış bir çekirdek üst bilgisi).

0 havuzundan değerleri okuma ve görüntüleme

Bu örnek, 0 havuzundaki KVP değerlerini okur ve görüntüler.

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "../include/linux/hyperv.h"

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

KVP_RECORD myRecords[200];

void KVPAcquireLock(int fd)
{
    struct flock fl = {F_RDLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int main (int argc, char **argv)
{
    char poolName[] = "/var/lib/hyperv/.kvp_pool_0";
    int   i;
    int   fd;
    int   bytesRead;
    int   numRecords;

    fd = open(poolName, O_RDONLY);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-30);
    }

    KVPAcquireLock(fd);
    bytesRead = read(fd, myRecords, sizeof(myRecords));
    KVPReleaseLock(fd);

    numRecords = bytesRead / sizeof(struct kvp_record);
    printf("Number of records : %d\n", numRecords);

    for (i = 0; i < numRecords; i++)
    {
        printf("  Key  : %s\n  Value: %s\n\n", myRecords[i].key, myRecords[i].value);
    }

    close(fd);

    return 0;
}

Havuz 1'de KVP öğesi oluşturma

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "../include/linux/hyperv.h"

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

void KVPAcquireWriteLock(int fd)
{
    struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int main (int argc, char **argv)
{
    char poolName[] = "/var/lib/hyperv/.kvp_pool_1";
    int   fd;
    KVP_RECORD newKvp;

    if (3 != argc)
    {
        printf("Usage: WritePool keyName valueString\n\n");
        exit (-5);
    }

    fd = open(poolName, O_WRONLY);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-30);
    }

    memset((void *)&newKvp, 0, sizeof(KVP_RECORD));
    memcpy(newKvp.key, argv[1], strlen(argv[1]));
    memcpy(newKvp.value, argv[2], strlen(argv[2]));

    KVPAcquireWriteLock(fd);
    write(fd, (void *)&newKvp, sizeof(KVP_RECORD));
    KVPReleaseLock(fd);

    close(fd);

    return 0;
}

Havuz 1'den bir KVP öğesini silme

Bu örnek bir öğeyi siler.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <uapi/linux/hyperv.h>

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

void KVPAcquireWriteLock(int fd)
{
    struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int find_record_offset(int fd, char *key)
{
    int bytesRead;
    int offset = 0;
    int retval = -1;

    KVP_RECORD kvpRec;

    while (1)
    {
        lseek(fd, offset, SEEK_SET);
        bytesRead = read(fd, &kvpRec, sizeof(KVP_RECORD));
        if (0 == bytesRead)
        {
            break;
        }

        if (0 == strcmp(key, (const char *) kvpRec.key))
        {
            retval = offset;
            break;
        }

        offset += sizeof(KVP_RECORD);
    }

    return retval;
}

int main (int argc, char **argv)
{
    char  poolName[] = "/var/lib/hyperv/.kvp_pool_1";
    int   fd;
    int   exitVal = -1;
    int   bytesRead;
    int   bytesWritten;
    int   offset_to_delete;
    int   offset_last_record;
    KVP_RECORD kvpRec;

    if (2 != argc)
    {
        printf("Usage: WritePool keyName valueString\n\n");
        exit (-5);
    }

    fd = open(poolName, O_RDWR, 0644);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-10);
    }

    KVPAcquireWriteLock(fd);
    offset_to_delete = find_record_offset(fd, argv[1]);
    if (offset_to_delete < 0)
    {
        exitVal = -15;
        goto cleanup2;
    }

    offset_last_record = lseek(fd, -sizeof(KVP_RECORD), SEEK_END);
    if (offset_last_record < 0)
    {
        exitVal = -20;
        goto cleanup2;
    }

    if (offset_last_record != offset_to_delete)
    {
        lseek(fd, offset_last_record, SEEK_SET);
        bytesRead = read(fd, &kvpRec, sizeof(KVP_RECORD));
        lseek(fd, offset_to_delete, SEEK_SET);
        bytesWritten = write(fd, &kvpRec, sizeof(KVP_RECORD));
    }

    ftruncate(fd, offset_last_record);

    exitVal = 0;

cleanup2:
    KVPReleaseLock(fd);

cleanup1:
    close(fd);

    return exitVal;
}

WMI kullanarak sunucudan anahtar-değer çiftleriyle çalışma.

Aşağıdaki örneklerde WMI v2 ad alanı kullanılır. WMI v1 (eski sürümler) için, segmenti \v2 ad alanı yolundan kaldırın.

Uyarı

Windows 8 veya Windows 8.1 kullanıyorsanız, ad alanlarını almak için İstemci Hyper-V yükleyin.

Ana bilgisayardan değeri okuma

Bu örnek adlı Statusbir VM'den anahtarın Vm1 değerini alır:

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName = 'Vm1'}
$vm.GetRelated("Msvm_KvpExchangeComponent").GuestExchangeItems | % { \
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(\
        "/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Status']")
    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(\
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()" ).Value
    }
}

Sunucudan anahtar-değer çiftleri ekleyin veya değiştirin

Ana bilgisayardan anahtar-değer çifti eklemek için, hem yönetim hizmetinin hem de VM'nin örneklerini alın ve Msvm_KvpExchangeDataItem yeni bir örnek oluşturun. Yeni örneği oluştururken Name, Data, ve Source (olmalıdır 0) belirtin. Ardından AddKvpItems fonksiyonunu çağırın.

Anahtar-değer çiftlerinin konak tarafından oluşturulmasıyla ilgili sorgulamalar, misafir sistem sorgularına benzer, ancak Msvm_KvpExchangeComponentSettingData için ek bir ilişkilendirme atlaması gerektirir. Değerleri değiştirmek ve silmek aynı şekilde çalışır; aynı anahtar adını belirtin ve uygun Modify veya Remove yöntemi çağırın.

Önemli

Aşağıdaki örneklerde v2 ad alanı kullanılır. Windows Server 2008 veya Windows Server 2008 R2 kullanıyorsanız, segmenti \v2 kaldırın.

Yeni anahtar-değer çifti ekleme

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data"
$kvpDataItem.Source = 0

$VmMgmt.AddKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Sunucuda anahtar-değer çiftlerini sorgula

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
($vm.GetRelated("Msvm_KvpExchangeComponent")[0] ).GetRelated("Msvm_KvpExchangeComponentSettingData").HostExchangeItems | % { \
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(\
        "/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Name2']")
    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(\
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()" ).Value
    }
}

Anahtar-değer çifti değiştirme

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data2"
$kvpDataItem.Source = 0

$VmMgmt.ModifyKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Anahtar-değer çiftini kaldır

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = [String]::Empty
$kvpDataItem.Source = 0

$VmMgmt.RemoveKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Ayrıca bakınız