![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=1739350878-psmR6ya27XpePzE1BBaiLuswdAyGZh16-0-61826eb753b08cb8726f961f0ccf36b2)
图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=1739350878-4rTQ8dMkWRWciJ8TNkEbGI4kLc1NzBRH-0-895541986b48abb930b99d661496da93)
图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=1739350878-8LWMEBzhbR9YWiRwwOK5Z5KgFBx2oNPS-0-c1b0b915b5f01591bad6577079dcb07f)
使用telnet连接localhost:1234。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/61_04.jpg?sign=1739350878-Y8SQUvAZrrYc1goGmD8OLObITIJlqzpg-0-e8ae93863f992f82ce02c895f1a772f5)
之后就可以使用qmp的命令和虚拟机交互了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_01.jpg?sign=1739350878-7jPur1EJZ19n96BybuL7buQKv5DUO9eK-0-04d4142e46da0bc59d011f928e75c0f8)
2.通过unix socket使用qmp
使用unix socket创建qmp。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_02.jpg?sign=1739350878-kXNKUeEFvfBylU2bEKIGAS85Qb57GUZI-0-ae286d25ca6460adf2485af76a496174)
使用nc连接该socket:
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_03.jpg?sign=1739350878-edDhKGaORIY289cyWhARkI1cMYO1pjku-0-d51a31c13b0576c930ecbdd5ea08268e)
之后就跟TCP一样,可以向其发送qmp命令了。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/62_04.jpg?sign=1739350878-84HztRNIeSCtAuX6GmstU5JwQzDaNOJv-0-74485bcc4fbeaf56df3a396f93d7ee17)
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=1739350878-OM9nMMqIfVOP7z5UmC32WsOUXGs3kCab-0-b47b86adf43c02a77a3dd1bcf53d4805)
这里以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=1739350878-od9RX5QbTP4pDkLIIjHgqD9TDDZaOXj0-0-ad0a2154a36a74e731466811a88240fd)
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=1739350878-XMrq5dWTPSh9DxRR7lzcmJZQcA6zgLAx-0-ef17b05521052d8e5dbce6bf94ae09ca)
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=1739350878-y5dNBoOTbBMQAYRvPnaSc560vKyRUY5w-0-8c8968082c24b1205b48aab3670fa61e)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_01.jpg?sign=1739350878-ZnOqbFjpkUS0OuTX5ktc1xNKiOfKc4cx-0-43b595901b73a7cbb76ae2cd27bd3d84)
就"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=1739350878-4fIbiiKlkSZRyOHqdcIfmwHok3myryr4-0-d5d6ee62cf2bee4dbc8744c371bca2bf)
接着,在qmp.c文件的最后实现“qmp-test”命令的处理函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/64_03.jpg?sign=1739350878-5KLibc3ThCr7Zq9DmQGdlsqxGjiC7Fp6-0-9223e074392944109a8f1867f7f32c0b)
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_01.jpg?sign=1739350878-tOBS2ks6Ga6qY5wYhFjWx7EGuUQxU9qy-0-da0c774a0ad46a8b7fedc0e1ae7cdaee)
这个时候可以使用如下的json命令向qmp发起功能请求。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_02.jpg?sign=1739350878-xAci86VcMwTq8pT0z5u7rPJ2lrHN67bc-0-fef960b646c4a4dbfae1781b27f45d5e)
将这个命令作为hmp也比较合适,这里也可以添加一个hmp命令,在hmp-commands.hx的中间添加下面的内容。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_03.jpg?sign=1739350878-mIJBtWnYtvkcyKEuyoJRn8D7X2fVaq78-0-718d5311684f27acf520826ff3e028bb)
在hmp.c的文件最后添加实现hmp命令功能的函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_04.jpg?sign=1739350878-e8llEJ5z6qYFEHkvutTVTXRVyvKTK1iU-0-f21fc421f3403f2d46e8a4fefd427dd6)
需要在hmp.h中声明一下该函数。
![](https://epubservercos.yuewen.com/81846C/20862584908972706/epubprivate/OEBPS/Images/65_05.jpg?sign=1739350878-Uygi2cxn7VcaYddx9QXcnht3jEObKhB4-0-09d17fdddd57a123476345fed55eb194)
重新编译QEMU之后,就能够使用“qmp-test 80”向QEMU发送hmp命令了。