- QEMU/KVM源码解析与应用
- 李强编著
- 1380字
- 2025-02-17 20:55:48
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=1739989319-RFTc6QWeCwV9DOEkP5xWV5duczDbC54P-0-ad8c1d965d3d2157267fcc190cc8f727)
图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=1739989319-XjW02PpmEkMwTEDPxQMX6YBB7VltuToE-0-9ee8fa9f4611a6c192045e3d1770d72d)
图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=1739989319-Vmajp3P0B47ZvICIUjLh7feCigsu145O-0-516acc404f7a0a01f728c3bdcdfe668f)
使用telnet连接localhost:1234。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_04.jpg?sign=1739989319-jsw4rVSLetOwPPAGiVznDXDdNatQ6Dkj-0-d12593ae4427c0206a89d431114e1b14)
之后就可以使用qmp的命令和虚拟机交互了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_01.jpg?sign=1739989319-hUoYGls9x0trVW04IOyPSL08SdZoMyrG-0-5e7a85f9b826fc7f53888cd75d3947ce)
2.通过unix socket使用qmp
使用unix socket创建qmp。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_02.jpg?sign=1739989319-l4l9OGGc1rCVebqcweTHvZCEb50sYnbe-0-6a44356af2b92e1cb908148bf4908502)
使用nc连接该socket:
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_03.jpg?sign=1739989319-Vo8LatSBaQUvLYdAwvgQ0Kf7wmlBk6jN-0-95e700510a5b473b922c4769973b12ca)
之后就跟TCP一样,可以向其发送qmp命令了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_04.jpg?sign=1739989319-0Qx3uWDH6PrBZge5zv9ZLWonZjE3LvfB-0-0bf9e12559e98f12089eda78db62986f)
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=1739989319-UTE8Tb7HsXk17oYZ5qbqoMvcMheXw2Yp-0-8852811f887497bb4520fa9bff9f38d5)
这里以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=1739989319-WJSam3qQton163Or9Ej7S0zIUz0CPoMf-0-f959d61cccb770b6e4827a7d51d3faf1)
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=1739989319-uUMoJATGNUiQHFGYT9d1g5oSaIrElkiB-0-8e800ae65c7e8e1bb0d899ea794e2dc3)
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=1739989319-qXjky3lrlagZG2f0MiDyjkW3WAH7rc0H-0-3d58926fc3bed8fbcadb34637b7793ce)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_01.jpg?sign=1739989319-Lntsk4grfvtTkrEpvaIcpbzXjzoogmuB-0-46d4b36f1a3125321d04160d4f3e17b3)
就"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=1739989319-SrdqoncatrZTNLWLsdHvoFnTh8X0zMni-0-eea72421e17d005afe91eda73150d7fd)
接着,在qmp.c文件的最后实现“qmp-test”命令的处理函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_03.jpg?sign=1739989319-yn4eE4U3QZuogZn9ctKLpOkLpYLT1W2b-0-fd07ca67c050b88fc8c8ea49825cb942)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_01.jpg?sign=1739989319-RrsNLT3pwaYuBDMzu8env3obZljfEXcR-0-bab2ac266e50db53d34454442be93bac)
这个时候可以使用如下的json命令向qmp发起功能请求。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_02.jpg?sign=1739989319-tLeUN4nMMwCgkiLyvtNrm68uLjzHkb7l-0-d83c1d7ef7739b2fff0a5f89b77af34b)
将这个命令作为hmp也比较合适,这里也可以添加一个hmp命令,在hmp-commands.hx的中间添加下面的内容。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_03.jpg?sign=1739989319-mwIomJtuBcIiRNJ4SygcX1DIqkFHfqno-0-c5aadbe8aa65dee9c206ed8b82560d93)
在hmp.c的文件最后添加实现hmp命令功能的函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_04.jpg?sign=1739989319-TyUzOtqNdmrUuEnXQGUVTqxwNxoNhCBe-0-42df799b22bb741dc8d39d3a898e78c0)
需要在hmp.h中声明一下该函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_05.jpg?sign=1739989319-Hvc3saldJfUxddY7nShenC6lE8wP0gII-0-9d40ee9cb9999d92838f944e9d03893d)
重新编译QEMU之后,就能够使用“qmp-test 80”向QEMU发送hmp命令了。