基于libvirt API监测xen初探

本文介绍了一个使用libvirt API开发的简易Xen监控工具myxm。该工具能够显示宿主机上正在运行的客户机名称及其CPU利用率和内存使用情况。文章详细展示了实现过程及关键代码,并与xm和virt-manager进行了对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机体系结构课程布置了大作业,通过使用Xen的API或封装了其API的函数库,如libvirt进行编程,实现以下功能:

  1. 以命令行形式显示宿主机(Host OS)上正在运行的客户机(Guest OS)名称;
  2. 通过命令行形式显示指定客户机(Guest OS)的工作状态(显示其 CPU 利用率,和内存使用情况即可);

这个作业工程类似于Fedora等Linux系统中内置的xm管理程序,在这里仅简单的实现xm top的功能。我选用了Fedora Core 8作为Host OS。在其上,通过Xen安装另一Fedora Core 8作为Guest OS。利用libvirt提供的API实现显示Guest OS名称、显示其 CPU 利用率,和内存使用情况的功能。并与xm、virt-manager的运行结果做对比,验证正确性。

安装所需的软件包:

  • xen-3.1.2-2.fc8
  • libvirt-o.4.2-1.fc8
  • libvirt-devl-0.4.2-1.fc8

其中,xen为虚拟机,libvirt为运行库,libvirt-devl为代码运行库,供开发编译使用。具体版本视安装源与系统其他组件的依赖关系。

在我使用Xen安装Guest OS的过程中出现的一点小问题:在Fedora Core 8中发现默认提供的SELinux服务会对xen产生影响,估计可能是阻碍了某些信号量的传递,通过修改/etc/sysconfig/selinux,设置其中SELINUX=disabled将SElinux禁用。

通过virt-manager或virt-install安装Guest OS。设置为256m内存,4G硬盘空间,半虚拟化方式。

有关libvirt API的使用:

virConnectPtr virConnectOpenReadOnly (const char * name)
在使用时首先应调用这个函数,获得hypervisor的链接
nameURI of hypervisor,为NULL则代表本地链接
Returns返回hypervisor的指针

int virConnectListDomains (virConnectPtr conn, int * ids, int maxids) 
获得hypervisor下所有域的ID 
connhypervisor链接指针
ids存储每个域ID的数组
maxids域数量的上限
Returns域的数量

virDomainPtr virDomainLookupByID (virConnectPtr conn, int id)
由域的ID返回域的指针 
connhypervisor链接指针
id域的ID
Returns返回域的指针

int virDomainGetInfo (virDomainPtr domain, virDomainInfoPtr info)
获得域的信息
domain域的指针
info指向存储域的信息的数据结构的指针
Returns成功获取返回0,否则返回-1

const char * virDomainGetName (virDomainPtr domain)
得到域的名字
domain域的指针
Returns域的名字

int virConnectClose (virConnectPtr conn)
释放hyperbisor的连接,如果程序出线异常或错误应调用本函数终止连接
connhypervisor的指针
Returns成功释放返回0,否则返回-1

int virDomainFree (virDomainPtr domain)
释放域的指针,如果连接到域就要先调用这个函数释放域的链接,之后再释放hyperbisor的连接
domain域的指针
Returns成功释放返回0,否则返回-1

一个主要的数据结构:

struct virDomainInfo
unsigned charstate当前域的运行状态
unsigned longmaxMem支持的最大内存
unsigned longmemory使用的内存
unsigned shortnrVirtCpu虚拟CPU数量
unsigned long longcpuTime虚拟CPU运行时间

我的设计思路是,利用virConnectOpenReadOnly()获取链接,通过virConnectListDomains()得到当前活跃的域的ID。对于每一个ID利用virDomainLookupByID()得到指向其域的指针。调用virDomainGetInfo()获得当前域的内存、CPU运行时间等信息。当前域的名称由virDomainGetName()获得。

CPU占用率的计算方法为:在myxm中两次调用virDomainGetInfo()获取时间段开始前和结束后的虚拟CPU运行时间,二者差值可知虚拟CPU的运行时间。间隔时间段用sleep()实现。在sleep()前后用gettimeofday()取得真实的时间差。该时间段内CPU运行时间与真实时间的比值即为这段时间内的CPU占用率。

编译时gcc增加-lvirt参数,用于包含libvirt-devl库。

/**
 * Project: myxm
 * Version: 0.2
 * Abstract: A simple xen monitor
 * Author: Gu Xiangnan
 * Date: 2008-05-25
 */

#include 
#include 
#include 

#define MAXID 50

/* the data structure of time */
typedef struct timeInfo
{
    long long cpu_time;
    struct timeval real_time;
} timeInfoNode;

/* the hypervisor connection */
static virConnectPtr conn = NULL;

/* release the connect of hypervisor */
void closeConn()
{
    if (conn != NULL)
        virConnectClose(conn);
}

/* release the domain pointer */
void freeDom(virDomainPtr dom)
{
    if (dom != NULL)
        virDomainFree(dom);
}

/* get the start time of each domain */
void getTimeInfo(int id, timeInfoNode * infos)
{
    virDomainPtr dom = NULL;
    virDomainInfo info;
    int ret;

    /* Find the domain of the given id */
    dom = virDomainLookupByID(conn, id);
    if (dom == NULL)
    {
        fprintf(stderr, "Failed to find Domain %d/n", id);
        freeDom(dom);
        closeConn();
    }

    /* Get the information of the domain */
    ret = virDomainGetInfo(dom, &info);
    if (ret < 0)
    {
        fprintf(stderr, "Failed to get information for Domain %d/n", id);
        freeDom(dom);
        closeConn();
    }

    /* get the start of realTime*/
    if (gettimeofday(&(infos->real_time), NULL) ==  - 1)
    {
        fprintf(stderr, "Failed to get start time/n");
			return;
    }

    /* get the start of CPUTime*/
    infos->cpu_time = info.cpuTime; /* nanosecond */

    freeDom(dom);
}

void getDomainInfo(int id, timeInfoNode infos)
{
    virDomainPtr dom = NULL;
    virDomainInfo info;
    int ret;
    struct timeval realTime;
    int cpu_diff, real_diff;
    float usage;

    /* Find the domain of the given id */
    dom = virDomainLookupByID(conn, id);
    if (dom == NULL)
    {
        fprintf(stderr, "Failed to find Domain %d/n", id);
        freeDom(dom);
        closeConn();
    } 

    /* Get the information of the domain */
    ret = virDomainGetInfo(dom, &info);
    if (ret < 0)
    {
        fprintf(stderr, "Failed to get information for Domain %d/n", id);
        freeDom(dom);
        closeConn();
    }

    /* get the end of realTime*/
    if (gettimeofday(&realTime, NULL) ==  - 1)
    {
        fprintf(stderr, "Failed to get start time/n");
        return;
    }

    /* calculate the usage of cpu */
    cpu_diff = (info.cpuTime - infos.cpu_time) / 10000;
    real_diff = 1000 *(realTime.tv_sec - infos.real_time.tv_sec) + 
        (realTime.tv_usec - infos.real_time.tv_usec);
    usage = cpu_diff / (float)(real_diff);

    /* print the results */
    printf("%d/t%.3f%/t%lu/t%lu/t%hu/t%0X/t%s/n", id, usage, info.memory / 1024,
        info.maxMem / 1024, info.nrVirtCpu, info.state, virDomainGetName(dom));

    freeDom(dom);
}

int main()
{
    int idCount;
    int i;
    int id;
    int ids[MAXID];
    timeInfoNode timeInfos[MAXID];

    printf("--------------------------------------------------------/n");
    printf("             XEN Domain Monitor Version 0.2/n");
    printf("             Build by Gu Xiangnan 35060514/n");
    printf("--------------------------------------------------------/n");

    /* NULL means connect to local Xen hypervisor */
    conn = virConnectOpenReadOnly(NULL);
    if (conn == NULL)
    {
        fprintf(stderr, "Failed to connect to hypervisor/n");
        closeConn();
        return 0;
    }

    /* get the count of IDs and save these ID into ids[] */
    idCount = virConnectListDomains(conn, &ids[0], MAXID);
    if (idCount < 0)
    {
        fprintf(stderr, "Failed to list the domains/n");
        closeConn();
        return 0;
    }

    printf("Domain Totals: %d/n", idCount);
    printf("ID/tCPU/tMEM/tMaxMEM/tVCPUs/tState/tNAME/n");

    /* loop get the CPUtime info by IDs */
    for (i = 0; i < idCount; i++)
    {
        id = ids[i];
        getTimeInfo(id, &(timeInfos[i]));
    }

    sleep(1);

    /* loop print the domain info and calculate the usage of cpus*/
    for (i = 0; i < idCount; i++)
    {
        id = ids[i];
        getDomainInfo(id, timeInfos[i]);
    }

    printf("--------------------------------------------------------/n");
    closeConn();
    return 0;
}

运行结果:

 

可以对比一下vmm与xm top,验证实现的正确性:

 

### 使用 libvirt API 进行虚拟化管理 #### 安装和配置环境变量 为了调试任何使用 `libvirt` 的程序,可以设置 `LIBVIRT_DEBUG=1` 环境变量。这有助于捕获详细的日志信息以便于排查问题[^1]。 ```bash export LIBVIRT_DEBUG=1 ``` #### 初始化连接到 Hypervisor 要开始使用 `libvirt` API,首先需要初始化与 hypervisor 的连接。Python 是一种常用的语言来操作此库: ```python import libvirt conn = libvirt.open('qemu:///system') if conn is None: print('Failed to open connection to qemu:///system') else: print('Successfully connected!') conn.close() ``` 这段代码尝试打开一个指向本地 QEMU/KVM hypervisor 的会话;如果成功,则打印一条消息并关闭连接[^3]。 #### 列举域 (Domain) 获取当前正在运行的所有虚拟机列表可以通过枚举活动域名完成: ```python domains = conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE) for domain in domains: print(f'Domain ID: {domain.ID()}, Name: {domain.name()}') ``` 上述脚本遍历所有活跃状态下的领域对象,并输出它们各自的ID以及名称。 #### 创建新域 创建一个新的虚拟机通常涉及定义 XML 描述符,该描述符指定了机器的各种属性(CPU 数量、内存大小等)。下面是一个简单的例子说明如何基于给定的XML字符串启动新的客户机实例: ```xml <domain type='kvm'> <name>example_vm</name> <memory unit='KiB'>524288</memory> <vcpu placement='static'>1</vcpu> ... </domain> ``` 接着,在 Python 中解析这个模板并通过API调用来激活它: ```python dom = conn.defineXML(xml_desc) if dom is not None: print('Domain created from XML.') dom.create() # 启动新建好的虚拟机 else: print('Failed to define a new domain') ``` 这里假设已经有一个有效的XML文档存储在变量 `xml_desc` 中。一旦定义好之后就可以通过 `.create()` 方法立即启动新建立起来的虚拟机了。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值