Sdílet prostřednictvím


Výměna dat: Sdílení informací mezi hostitelem a hostem na Hyper-V pomocí párů klíč-hodnota

Výměna dat je integrační služba (označovaná také jako výměna páru klíč-hodnota nebo KVP), která se dá použít ke sdílení malých informací mezi virtuálním počítačem (hostem) a jeho hostitelem Hyper-V. Obecné informace o virtuálním počítači a hostiteli se automaticky vytvoří a uloží jako páry klíč-hodnota. Můžete také vytvořit vlastní páry pro sdílení vlastních dat.

Páry klíč-hodnota se skládají z klíče a hodnoty. Oba jsou řetězce; nejsou podporovány žádné jiné datové typy. Když se vytvoří nebo změní pár klíč-hodnota, zobrazí se hostu i hostiteli. Data KVP putují přes Hyper-V VMbus a nevyžadují připojení k síti mezi hostem a hostitelem.

Páry klíč-hodnota zůstanou po vytvoření, dokud nebudou odstraněny. Každá aplikace, která vytváří páry klíč-hodnota, by je měla odstranit, když už je nepotřebujete. Páry klíč-hodnota se během Live Migration přesunou s virtuálním počítačem.

Hostované systémy Windows

U hostů Windows jsou data KVP uložená v registru v části:

HKLM\SOFTWARE\Microsoft\Virtual Machine

Data jsou uspořádaná do těchto podklíčů:

  • Virtual Machine\Auto – data popisující hosta. Po načtení se vytvoří ovladače integrační služby. Viditelné pro hostitele jako vnitřní data.
  • Virtuální počítač\Externí – Data odsílaná hostu z hostitele uživatelem.
  • Virtual Machine\Guest – data vytvořená na hostu. Viditelné pro hostitele jako nevnitřně uložená data.
  • Virtual Machine\Guest\Parameter – data odesílaná z hostitele k hostu, která popisuje hostitele.

Přidání hodnot uvnitř hosta je stejně jednoduché jako vytvoření nové řetězcové hodnoty pod HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest. Abyste mohli toto umístění upravit, musíte být správcem hosta. K načtení hodnoty můžete použít rozhraní WMI (PowerShell nebo jiné nástroje) z hostitele nebo vzdáleného počítače (s oprávněními).

Informace o omezení velikosti registru naleznete v článku (starší verze) Omezení velikosti elementů registru.

Přidejte nový pár klíč-hodnota na hostu

V tomto příkladu je hodnota Status nastavena na Ready:

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

Ke změně hodnoty můžete použít stejnou syntaxi.

Vyhledávání párů klíč-hodnota v hostitelském prostředí

Dotazování na hodnotu externího podklíče (data vložená do hosta z hostitele):

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

Linuxové hosty

Linux nemá registr, takže položky KVP jsou uložené v systému souborů. Proces démona, hv_kvp_daemon, musí být spuštěn, aby mohlo probíhat zpracování. U většiny distribucí s linuxovými integračními službami (LIS) nebo s ovladači, které jsou v jádru, se démon spustí automaticky. V některých případech může být potřeba provést další kroky pro instalaci a spuštění démona.

Integrační služby Linuxu implementují výměnu dat s fondy KVP. Fond KVP je soubor uložený na konkrétní cestě. Existují čtyři soubory poolu:

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

Tyto soubory fondu odpovídají následujícím sadám klíčů registru Windows:

  • Bazén 0: Virtual Machine\External
  • Bazén 1: Virtual Machine\Guest
  • Bazén 2: Virtual Machine\Auto
  • Bazén 3: Virtual Machine\Guest\Parameter

Poznámka:

Další informace o podpoře KVP pro Linux naleznete v tématu Linux a FreeBSD Virtual Machines na Hyper-V.

Infrastruktura páru klíč-hodnota nemusí fungovat správně bez aktualizace softwaru linuxu. Pokud narazíte na problémy, obraťte se na dodavatele distribuce a požádejte ho o aktualizaci.

Struktura fondu

Každý soubor fondu obsahuje záznamy s touto strukturou:

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

Tyto konstanty velikosti jsou definovány v hyperv.h (hlavička jádra distribuovaná se zdroji jádra Linuxu).

Čtení a zobrazení hodnot z poolu 0

Tato ukázka čte hodnoty KVP z poolu 0 a zobrazí je.

```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;
}

Vytvořte položku KVP ve fondu 1

#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;
}

Odstraňte položku KVP ze zásobníku 1

Tento příklad odstraní položku.

#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;
}

Práce s páry klíč-hodnota z hostitelského systému pomocí WMI

Následující příklady používají obor názvů WMI v2. Pro rozhraní WMI v1 (starší verze) odeberte \v2 segment z cesty k jmennému prostoru.

Poznámka:

Pokud používáte Windows 8 nebo Windows 8.1, nainstalujte klientskou Hyper-V, abyste získali obory názvů.

Přečíst hodnotu z hostitele

Tento příklad získá hodnotu klíče Status z virtuálního počítače s názvem Vm1:

$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
    }
}

Přidání nebo úprava párů klíč-hodnota z hostitele

Pokud chcete přidat pár klíč-hodnota z hostitele, získejte instance služby pro správu i virtuálního počítače a vytvořte novou instanci Msvm_KvpExchangeDataItem. Při vytváření nové instance zadejte Name, Dataa Source (musí být 0). Pak zavolejte AddKvpItems.

Dotazování na páry klíč-hodnota vytvořené hostitelem se podobá dotazům hosta, ale vyžaduje další skok asociace.Msvm_KvpExchangeComponentSettingData Úpravy a odstraňování hodnot fungují stejným způsobem – zadejte stejný název klíče a volejte odpovídající Modify metodu nebo Remove metodu.

Důležité

Následující příklady používají obor názvů v2. Pokud používáte Windows Server 2008 nebo Windows Server 2008 R2, odeberte \v2 segment.

Přidání nového páru klíč-hodnota

$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))

Dotazování párů klíč-hodnota na hostiteli

$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
    }
}

Úprava páru klíč-hodnota

$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))

Odebrání páru klíč-hodnota

$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))

Viz také