
2.5 hmp与qmp介绍
2.5.1 hmp与qmp
QEMU程序在运行时提供了一个所谓的监控器(monitor)来跟外界进行数据交互。QEMU monitor有很多功能,如得到虚拟机运行的一些统计信息、进行设备的热插拔、动态设置一些参数、开启一些功能等。QEMU monitor能够使用多种方式进行交互,如QEMU的控制台、TCP网络、UNIX套接字、文件等。
与QEMU monitor进行交互的协议有两类,传统的是基于字符串的协议,叫作Human Monitor Protocol(HMP),其功能比较简单,可用于进行简单的调试和查看虚拟机状态等。其基本原理如图2-23所示。

图2-23 hmp原理
另一个协议是QEMU Monitor Protocol(qmp),它是一个基于json、用来与QEMU进行交互的协议,采用典型的服务器-客户端架构。通过qmp,上层管理软件可以很方便地对QEMU虚拟机进行管理,如virsh就能够使用qmp对虚拟机进行管理。qmp原理如图2-24所示。

图2-24 qmp原理
现在的QEMU底层其实都是通过qmp完成功能的,只是还保留了hmp的接口。
从图2-23可以看出,hmp是针对人的,所以采用了基于“info xxx”等简单易记字符串的协议,而qmp主要是针对机器和其他程序的,所以采用了更加规范的json格式来传递数据。
2.5.2 qmp的使用
1.通过TCP使用qmp
使用-qmp添加qmp相关参数:

使用telnet连接localhost:1234。

之后就可以使用qmp的命令和虚拟机交互了。

2.通过unix socket使用qmp
使用unix socket创建qmp。

使用nc连接该socket:

之后就跟TCP一样,可以向其发送qmp命令了。

qmp的详细命令格式可以在QEMU代码树主目录下面的qmp-commands.hx中找到。
2.5.3 qmp源码分析
与qmp参数相关的解析函数是monitor_parse,从vl.c可以看到,多个命令都会引起monitor参数的解析。

这里以qmp为例介绍,其参数是-qmp unix:/tmp/qmp-test,server,nowait。
由于解析过程比较烦琐并且脱离主题,因此这里只进行简单介绍。在解析qmp命令时会创建一个-chardev参数,解析chardev参数的时候会创建chardev设备,然后根据所指定的unix地址,最终创建一个unix socket,代码如下。

socket_listen返回一个新创建的fd,这个fd会被添加到QEMU的主程序循环中进行事件监听,这样qmp的unix socket就处在监听状态了,其接收连接的函数是tcp_chr_accept,客户端可以去连接它并且进行数据交互。
使用nc进行连接。

tcp_chr_accept会调用tcp_chr_new_client将之前的监听取消,然后tcp_chr_new_client调用tcp_chr_connect,设置新的监听函数来对这个连接进行处理,此时这个socket的监听函数为tcp_chr_read。
qmp连接好之后的第一步是协商,客户端通过发送{"execute":"qmp_capabilities"}完成。经过tcp_chr_read的一系列调用,最终会调用到handle_qmp_command。handle_qmp_command调用qmp_dispatch->do_qmp_dispatch,最后一个函数调用cmd->fn,从而实现命令的处理函数,其中cmd是注册的qmp命令,用QmpCommand表示。


就"qmp_capabilities"命令来说,do_qmp_dispatch函数最终会调用到qmp_qmp_capabilities。几乎所有qmp命令的处理函数形式都是qmp_xxx_yyy,后面的xxx和yyy表示对应的qmp命令。
2.5.4 qmp命令添加
这里简单介绍了qmp的原理,实际中其实很多时候需要添加一个qmp来定制一些功能。这里以一个例子介绍如何添加qmp命令。添加一个qmp命令包括如下4个步骤。
1)定义符合QAPI方式的qmp命令及其参数和返回值的类型。
2)完成新增qmp的功能函数,既可以将这个函数放在相关功能的模块,也可以放在qmp.c文件中。
3)此时完成了一个qmp命令的编写,可以通过2.5.2节的方式调用该qmp功能。
4)编写相应的hmp命令。这不是一个必需的步骤,只有在该命令对human有意义的时候才需要编写。hmp功能函数直接调用对应的qmp函数。
比如要添加一个“qmp-test”的qmp命令,执行该命令的时候会设置一个全局变量。第一步在qapi-schema.json文件的最后一行添加如下内容。

接着,在qmp.c文件的最后实现“qmp-test”命令的处理函数。


这个时候可以使用如下的json命令向qmp发起功能请求。

将这个命令作为hmp也比较合适,这里也可以添加一个hmp命令,在hmp-commands.hx的中间添加下面的内容。

在hmp.c的文件最后添加实现hmp命令功能的函数。

需要在hmp.h中声明一下该函数。

重新编译QEMU之后,就能够使用“qmp-test 80”向QEMU发送hmp命令了。