![QEMU/KVM源码解析与应用](https://wfqqreader-1252317822.image.myqcloud.com/cover/969/40107969/b_40107969.jpg)
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所示。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_01.jpg?sign=1739268147-oNmNcYhOcqtdbmF6ACk5Tv1WrqPdHTrf-0-e93b7d917704a1fd4c9e22a16ca366bb)
图2-23 hmp原理
另一个协议是QEMU Monitor Protocol(qmp),它是一个基于json、用来与QEMU进行交互的协议,采用典型的服务器-客户端架构。通过qmp,上层管理软件可以很方便地对QEMU虚拟机进行管理,如virsh就能够使用qmp对虚拟机进行管理。qmp原理如图2-24所示。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_02.jpg?sign=1739268147-NksGzTEJBe2HQNDgVWx1Vi6jwu3nzXXq-0-40e623c732daa433df294c2577374655)
图2-24 qmp原理
现在的QEMU底层其实都是通过qmp完成功能的,只是还保留了hmp的接口。
从图2-23可以看出,hmp是针对人的,所以采用了基于“info xxx”等简单易记字符串的协议,而qmp主要是针对机器和其他程序的,所以采用了更加规范的json格式来传递数据。
2.5.2 qmp的使用
1.通过TCP使用qmp
使用-qmp添加qmp相关参数:
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_03.jpg?sign=1739268147-kvz2RJFjYgXC3GdenOHkstS5kOB6qlxz-0-e4456433496209a73a26d637d088cf71)
使用telnet连接localhost:1234。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_04.jpg?sign=1739268147-UkOdM75XmGA0FUZywB1UMQW2zsuBDB75-0-15675ae4c220f5866b1684d8d2ad9768)
之后就可以使用qmp的命令和虚拟机交互了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_01.jpg?sign=1739268147-7dZCI4lnko5wfg0Bz7mSKI1PeVLs8IN5-0-0021e06d154ff4e636679cbf47cfb22b)
2.通过unix socket使用qmp
使用unix socket创建qmp。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_02.jpg?sign=1739268147-9mWoxazXQHZLWKwODWMJIb7dQOvxPPqD-0-b9e8ee30e03f3a8a5adb641877d88db6)
使用nc连接该socket:
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_03.jpg?sign=1739268147-lLFGM49GUPB4BQW6BqBBgrmiObBvgM3S-0-14b49aa68864291dac6c0cc734033e20)
之后就跟TCP一样,可以向其发送qmp命令了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_04.jpg?sign=1739268147-ZN8Srqb4CeR0ANsdqiYfcUwoQQngXHfV-0-aef7508961ac550ec1f388f6bcf8d1c9)
qmp的详细命令格式可以在QEMU代码树主目录下面的qmp-commands.hx中找到。
2.5.3 qmp源码分析
与qmp参数相关的解析函数是monitor_parse,从vl.c可以看到,多个命令都会引起monitor参数的解析。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_05.jpg?sign=1739268147-FIwxDSslGltl7peGMdkM7Ip0aXXat3sS-0-784816da17f119ea766592f2f33eb12b)
这里以qmp为例介绍,其参数是-qmp unix:/tmp/qmp-test,server,nowait。
由于解析过程比较烦琐并且脱离主题,因此这里只进行简单介绍。在解析qmp命令时会创建一个-chardev参数,解析chardev参数的时候会创建chardev设备,然后根据所指定的unix地址,最终创建一个unix socket,代码如下。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/63_01.jpg?sign=1739268147-oM2T7juuZMRL7w2pGRlTeFd0s9a2EKwm-0-ae71d326461643e76ff62349d783bdbb)
socket_listen返回一个新创建的fd,这个fd会被添加到QEMU的主程序循环中进行事件监听,这样qmp的unix socket就处在监听状态了,其接收连接的函数是tcp_chr_accept,客户端可以去连接它并且进行数据交互。
使用nc进行连接。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/63_02.jpg?sign=1739268147-M6nNXCKlzP2FsbddcOvYwy9Bgd0JRvW3-0-a514379183fc34139b49c61bb1c9dcb8)
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表示。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/63_03.jpg?sign=1739268147-0zfAyOUPnQkJxmz5XARMbA8Ru1i5Qb89-0-a602eea6177888c1d4b8afb00f1b3e33)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_01.jpg?sign=1739268147-AkAWHewVQSJfWXY9RSRmg4bi6O1QmIzQ-0-c702c1635f6b2bd37bb5b9b7e1a80123)
就"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文件的最后一行添加如下内容。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_02.jpg?sign=1739268147-yjZSb5BAXVrKYhWc7ilniUfjD7ce4T03-0-9d619d54c808ba7f335441fa61d87a35)
接着,在qmp.c文件的最后实现“qmp-test”命令的处理函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_03.jpg?sign=1739268147-Br4lfbxlQOrjjXdFtuZDhmRAXoN5FUMK-0-98a457886d3622b85cdbc6711cf8b0d6)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_01.jpg?sign=1739268147-03KAf3eLDqLYrwcVIumoKpGDkQma9LbU-0-26a7d83b14901b0c3f02326f0624630e)
这个时候可以使用如下的json命令向qmp发起功能请求。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_02.jpg?sign=1739268147-OlPm7zEf5zA3K4uIGlON2Zmw6oIBonk9-0-18ab5a12d3eab225faacadd3d21b106a)
将这个命令作为hmp也比较合适,这里也可以添加一个hmp命令,在hmp-commands.hx的中间添加下面的内容。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_03.jpg?sign=1739268147-d5oT855NGbfWvpdpR39T166HUqZfir6o-0-259361556611f503837206dcd57f5eca)
在hmp.c的文件最后添加实现hmp命令功能的函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_04.jpg?sign=1739268147-JxfaPCU9q2iTKONkpLAcwHPmzUSc8Alm-0-92adcf35170e87afe94bb7b77c60388e)
需要在hmp.h中声明一下该函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_05.jpg?sign=1739268147-ylrWdlmsowBVkgGP5c22rtwY9qEfUTg3-0-e794a3acc2f839dfd8644131292d708c)
重新编译QEMU之后,就能够使用“qmp-test 80”向QEMU发送hmp命令了。