QEMU QMP机制

Zihao Chang2020-11-29QEMUQMPHMP

QMP简介

QMP的全称是QEMU Machine Protocol,是一种以json格式为基础的协议,允许用户通过该接口查询、配置QEMU实例。

QEMU官方文档中对QMP的解释是:

  • Lightweight, text-based, easy to parse data format
  • Asynchronous messages support (events)
  • Capabilities negotiation
  • API/ABI stability guarantees

很多基于QEMU的应用都使用了QMP接口,比如著名的虚拟化中间件libvirt,对QEMU实例的操作就是使用了QMP接口。此外,还可以通过telnet、qmp-shell script等方式使用QMP接口,QEMU的官方文档进行了详细介绍。本博客主要使用libvirt来展示QMP相关接口,通过libvirt提供的virsh工具可以直接调用QMP接口。

1 环境准备

  • 支持虚拟化的Server,装有QEMU、libvirt等相关虚拟化组件
  • 虚拟机,可以通过virsh与其交互

2 相关命令使用

2.1 通过virsh list查看虚拟机状态是否正常

Linux:~ # virsh list
 Id   Name     State
------------------------
 1    ubuntu   running

2.2 查看qemu-monitor-command子命令

Linux:~ # virsh qemu-monitor-command --help
  NAME
    qemu-monitor-command - QEMU Monitor Command

  SYNOPSIS
    qemu-monitor-command <domain> [--hmp] [--pretty] [--return-value] {[--cmd] <string>}...

  DESCRIPTION
    QEMU Monitor Command

  OPTIONS
    [--domain] <string>  domain name, id or uuid
    --hmp            command is in human monitor protocol
    --pretty         pretty-print any qemu monitor protocol output
    --return-value   extract the value of the 'return' key from the returned string
    [--cmd] <string>  command

qemu-monitor-command子命令是virsh调用QMP的接口,通过帮助文档我们可以看到其基本用法。 其中 --pretty 可以格式化相关json输出, --hmp 是将复杂的QMP接口简化成了便于手动执行的接口,--return-value仅输出返回内部,忽略状态码等信息。

2.3 调用示例

  1. virsh qemu-monitor-command ubuntu '{"execute":"query-commands"}'
Linux:~ # virsh qemu-monitor-command ubuntu '{"execute":"query-commands"}' 
{"return":[{"name":"netdev_add"},{"name":"device_add"},{"name":"query-QMP-schema"},
{"name":"query-memory-size-summary"},{"name":"closefd"},{"name":"getfd"},
{"name":"set_link"},{"name":"query-uuid"},{"name":"query-kvm"},{"name":"query-name"},
    ···
    忽略部分输出
    ···
{"name":"add_client"},{"name":"query-commands"},{"name":"query-version"},{"name":"QMP_capabilities"}],"id":"libvirt-21"}

可以看到QMP提供了非常多的接口供使用,包括query-commandsquery-qmp-schemaquery-kvm等查询类接口,add_clientset_link等修改设置的接口。

  1. virsh qemu-monitor-command ubuntu --pretty '{"execute":"query-memory-size-summary"}'
Linux:~ # virsh qemu-monitor-command ubuntu --pretty '{"execute":"query-memory-size-summary"}'
{
  "return": {
    "base-memory": 4294967296,
    "plugged-memory": 0
  },
  "id": "libvirt-30"
}

加入 --pretty参数后,输出被格式化。

2.4 HMP格式

上面提到qemu-monitor-command子命令支持 --hmp参数,是对QMP命令的简化,大大降低了使用QMP命令的复杂度,并且部分HMP就是对QMP命令进行了封装,底层实际只用的还是QMP命令。但是通过QEMU的官方说明看,HMP是QEMU上的简单交互式监视器,主要为调试和简单的人类使用而设计,更高级别的工具应该连接到QMP , QMP才能提供一个稳定的JSON接口,以便于进行可靠的解析。 以查询虚拟机内存为例,使用HMP可以简化命令的输入和输出:

Linux:~ # virsh qemu-monitor-command ubuntu --HMP info memory_size_summary
base memory: 4294967296
plugged memory: 0

3 编写新的QMP命令

下面将展示如何向QEMU中增加一个hello world接口。

3.1 QEMU源码编译安装

这里仅展示了基础步骤,还要根据不同的平台和机器安装对应的依赖。

git clone https://git.qemu.org/git/qemu.git
cd qemu
git submodule init
git submodule update --recursive
./configure
make

3.2 编写QMP接口源码

3.2.3 增加QMP接口

QEMU提供了一套QMP接口实现框架,在QEMU的源码的qapa路径下有多个***.json, 这些都是不同分类的QMP接口的定义。这里选择在qapi/misc.json文件,增加一个hell world接口声明。

##
# @hello-world:
# Print a client provided string to the standard output stream.
#
# @message: string to be printed
#
# Returns: Nothing on success.
#
# Notes: if @message is not provided, the "Hello, world" string will
#        be printed instead
# Since: <next qemu stable release, eg. 1.0>
##
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
3.2.3 实现QMP函数

在monitor/qmp-cmds.c添加函数实现

void qmp_hello_world(bool has_message, const char *message, Error **errp)
{
    if (has_message) {
        printf("%s\n", message);
    } else {
        printf("Hello, world\n");
    }
}
3.2.4 实现HMP命令

在hmp-commands.hx添加接口

    {
        .name       = "hello-world",
        .args_type  = "message:s?",
        .params     = "hello-world [message]",
        .help       = "Print message to the standard output",
        .cmd        = hmp_hello_world,
    },

在include/monitor/hmp.h添加函数声明

void hmp_hello_world(Monitor *mon, const QDict *qdict);

在monitor/hmp-cmds.c添加函数实现

void hmp_hello_world(Monitor *mon, const QDict *qdict)
{
    const char *message = qdict_get_try_str(qdict, "message");
    Error *err = NULL;

    qmp_hello_world(!!message, message, &err);
    if (err) {
        monitor_printf(mon, "%s\n", error_get_pretty(err));
        error_free(err);
        return;
    }
}
3.2.5 验证QMP命令实现

编写完成上述代码后,重现编译安装,修改虚拟机xml中的<emulator>标签,使用自定义的QEMU启动虚拟机,验证新的QMP命令。

  1. 查看QMP命令是否存在
Linux:~ # virsh qemu-monitor-command ubuntu --pretty '{"execute":"query-commands"}' | grep hello
      "name": "hello-world"
  1. 调用QMP命令
Linux:~ # virsh qemu-monitor-command ubuntu --pretty '{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }'
{
  "return": {

  },
  "id": "libvirt-17"
}
  1. 查看HMP命令是否存在
Linux:~ # virsh qemu-monitor-command ubuntu --hmp help | grep hello
hello-world hello-world [message] -- Print message to the standard output

【免责声明】本文仅代表作者本人观点,与本网站无关。本网站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文仅供读者参考,由此产生的所有法律责任均由读者本人承担。