qmp—QEMU Machine Protocol介绍_北方南方-程序员宅基地

技术标签: 虚拟化  qmp  qemu  

1、基于JSON(http://www.ietf.org/rfc/rfc4627.txt)的协议,便于其他应用控制QEMU

2、QMP本质上是一种unix socket本地通信机制,通信内容基于JSON格式,当其他的应用成功连接qemu的QMP监听服务后,会得到一个欢迎消息(表明连接成功),具体的消息如下:

{ "QMP": {"version": json-object, "capabilities": json-array } }

其中,json-object是一个JSON的对象,json-array是一个JSON对象的数组,一个JSON对象一般表现为一个字典。JSON格式是一种键值对的格式,键值version指定QEMU的版本号,键值capabilities指定QEMU的QMP机制提供全部功能。

3、发送命令格式

{"execute": json-string, "arguments": json-object,"id": json-value }

键值execute指定要执行的命令;键值arguments指定命令执行时候需要的参数,是可选的一个键值;键值id是该执行命令的一个标识,是可选的,如果发送命令中包括这个键值,那么返回的响应中也会有同样的键值。

4、响应格式
(1)成功响应

{"return": json-object, "id": json-value }

键值return返回命令执行后的结果数据,如果命令执行完毕没有返回数据,则该键值对应的内容为空;键值id返回对应的发送命令中的键值id对应的值。

(2)错误响应

{"error": { "class": json-string, "desc":json-string }, "id": json-value }

键值class指定错误的类型;键值desc指定人类可读的错误描述;键值id意思同上。

5、异步事件
由于状态改变,QEMU可以单方面向应用发送消息,被称之为异步事件。格式如下:

  {
   "event": json-string, "data": json-object,
   "timestamp": { "seconds": json-number,"microseconds": json-number }
  }

键值event包括事件的名字;键值data指定事件关联的数据,可选的;键值timestamp指定了事件发生的具体时间。
对于发生的事件可以参考qmp-event.txt。

6、QMP样例

6.1. QEMU的欢迎消息

{ 
 "QMP": { 
    "version": {
        "qemu": { 
            "micro": 50, "minor": 6,"major": 1 
        }, 
        "package": ""
    },
    "capabilities": []
 }
}

6.2. 执行stop命令

C: { "execute": "stop" } 
S: {"return": {} }

C代表client,即其他应用;S代表server,即QEMU。

6.3. 查询KVM信息

C: { "execute": "query-kvm","id": "example" } 
S: { "return": {"enabled": true, "present": true }, "id":"example"}

6.4. 错误返回

C: { "execute": } 
S: { "error":{ "class": "GenericError", "desc": "InvalidJSON syntax" } }

6.5. 掉电事件

S: { "timestamp": { "seconds":1258551470, "microseconds": 802384 }, "event":"POWERDOWN" }

7、Capabilities Negotiation模式
当一个应用连接到QEMU后,QEMU将会处于Capabilities Negotiation模式,在该模式下,只有qmp_capabilities命令可以被使用,当应用发送qmp_capabilities命令并且QEMU成功执行该命令后,QEMU将会处于Command模式,此时,除了qmp_capabilities之外的其他命令便可以使用了。

8、使用QAPI框架编写QMP接口

8.1. 一般步骤:

(1)在文件qapi-schema.json中写命令的类型声明;

(2)编写QMP命令本身,这是一个常规的C函数。 优选地,该命令应该由一些QEMU子系统导出。 但它也可以直接添加到qmp.c文件中。

(3)到此为止,这个QMP命令可以在QMP协议下被测试。

(4)等效的编写HMP命令,但这不是必须的步骤。

8.2. 测试方式:

(1)启动QEMU

#/path/to/your/source/qemu [...] -chardev socket,id=qmp,port=4444,host=localhost,server \
     -mon chardev=qmp,mode=control,pretty=on

将会得到以下输出:
这里写图片描述
(2)连接QEMU

$ telnet localhost 4444

这里写图片描述
(3)输入使能QMP命令

{ "execute": "qmp_capabilities"}

这里写图片描述
8.3. 编写一个QMP命令不返回任何数据

(1)在qapi-schema.json的尾部添加以下内容:

{ 'command': 'hello-world' }

指定一个新的QMP命令,编译的过程中会自动生成该命令的JSON格式的解析函数。

(2)在qmp.c中添加命令的实现:

void qmp_hello_world(Error **errp) {printf("Hello, world!\n"); }

注意:命令必须是以qmp_开头,void代表该命令不返回任何内容,参数errp是用来返回错误信息的,如果函数不返回任何错误信息,请不要使用该参数。没有必要添加该函数的原型声明,因为qapi可以自动化函数原型声明的任务。

(3)在文件qmp-commands.hx中添加以下内容:

{
    .name  = "hello-world",
    .args_type  = "",
    .mhandler.cmd_new = qmp_marshal_input_hello_world,
},

注意:在文件qmp-commands.hx中,注释内容放在SQMP和EQMP之间。

(4)进行编译

make

(5)按照测试时步骤,输入{“execute”: “hello-world” }后,如下:

这里写图片描述
QEMU端的输出
这里写图片描述
8.4. 添加参数
给命令添加一个参数—message。
(1)改变命令的声明方式(qapi-schema.json):

{ 'command': 'hello-world', 'data': { '*message': 'str' } }
注意:*是为了标识该参数是可选的。

(2)更新函数的具体实现(qmp.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"); }
}

注意:每一个可选的参数都有一个hash_开头的bool类型变量,该变量表明该可选参数有没有值,另外,函数中参数的顺序必须和.json文件中键值data中定义的一样。
(3)更新文件qmp-commands.hx中相应的内容。

{ 
.name       = "hello-world",
        .args_type  = "message:s?",
        .mhandler.cmd_new = qmp_marshal_input_hello_world, 
},
args_type字段解释了该QMP命令的参数情况,其中s表示是字符串,?表示该参数是可选的。

(4)编译测试

{ "execute": "qmp_capabilities" }
{ "execute": "hello-world" }

这里写图片描述

{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }

这里写图片描述
QEMU端的输出:
这里写图片描述
8.5. 返回错误信息
使用头文件error.h导出的错误信息输出接口,错误信息通过函数error_set()来进行设置。假设hello-world命令不准发送包含love字符串的消息,如果发送的消息包含love字符串,则返回错误信息。
(1)只在qmp.c中对函数实现进行修改即可,如下:

void qmp_hello_world(bool has_message, const char *message, Error **errp) { 
    if (has_message){ 
        if(strstr(message, "love")){ 
            error_set(errp, ERROR_CLASS_GENERIC_ERROR, "the word 'love' is not allowed");
            return;
        }
        printf("%s\n", message); 
    } else { 
        printf("Hello, world\n");
    }
}

error_set()的第一个参数是错误信息指针,该指针是所有QMP函数的一个参数,第二个参数是错误的类型,第三个参数就是错误的具体描述。所有存在的错误类型的值都被定义在文件qapi-schema.json中。
(2)编译测试

{ "execute": "qmp_capabilities" }
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }

这里写图片描述
8.6. 添加命令说明文档
在文件qapi-schema.json中添加hello-world命令的使用说明,如下:

## 
# @hello-world 
# 
# Print a client provided string to the standard output stream. 
# 
# @message: 
#optional 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' } }

8.7. 命令返回值
一个QMP命令可以返回其支持的各种类型数据,包括:int, str, bool, enumerations and user defined types。接下来对如何返回自定义数据进行说明。
(1)在文件qapi-schema.json中定义数据结构类型和命令格式

{ 'type': 'QemuAlarmClock', 'data': { 'clock-name': 'str', '*next-deadline': 'int' } }

键值type定义一个新的QAPI数据类型,键值data包含数据结构的成员,每个成员都是基本类型(int, str, uint8, uint16, uint32),其中next-deadline是一个可选的返回值。
定义命令格式:

{'command':'hello-world', 'data':{'*message':'str'}, 'returns': 'QemuAlarmClock'}

(2)函数的具体实现

QemuAlarmClock *qmp_hello_world(bool has_message, const char *message, Error **errp) { 
    if (has_message){ 
        if(strstr(message, "love")){ 
            error_set(errp, ERROR_CLASS_GENERIC_ERROR, "the word 'love' is not allowed");
            return;
        }
            printf("%s\n", message); 
    } else { 
        printf("Hello, world\n");
    }
    QemuAlarmClock *clock;
    int64_t deadline = 1234567;//for testing
    clock = g_malloc0(sizeof(*clock));
    if (deadline > 0) {
        clock->has_next_deadline = true;//可选参数的标记
        clock->next_deadline = deadline;
     }
    char name = “huhahuha\n”;//for testing
    clock->clock_name = g_strdup(name);
    return clock;
}

(3)编译测试

{ "execute": "qmp_capabilities" }
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
{ "execute": "hello-world", "arguments": { "message": "We like qemu" } }

这里写图片描述
8.8. 返回值列表
QAPI在自动生成QemuAlarmClock类型的时候,同时会生成该类型的一个链表结构,可以生成该类型返回值的一个列表,自动生成的链表结构被命名为QemuAlarmClockList(在类型名字最后添加了List)。于是,我们可以通过该列表返回更多的相关信息。
(1)修改返回值定义如下(qapi-schema.json):

{ 'command': 'hello-world', 'data': { '*message': 'str' }, 'returns': ['QemuAlarmClock']}
注意:[]表示列表

(2)更新函数的具体实现,如下:

QemuAlarmClockList *qmp_hello_world(bool has_message, const char *message, Error **errp)
{
    if(has_message){
        if(strstr(message, "love")){
            error_set(errp, ERROR_CLASS_GENERIC_ERROR, "the word 'love' is not allowed!\n");
            return NULL;
        }
        printf("%s\n", message);
    }
    else{
        printf("Hello, world!\n");
    }
    QemuAlarmClockList *clockList = NULL, *clock;
    int64_t deadline;
    char name[] = "huhahuha";//for testing
    for(deadline = 0x0; deadline < 0x5; deadline ++){
        clock = g_malloc0(sizeof(*clock));
        clock->value = g_malloc0(sizeof(*clock->value));
        if (deadline > 0) {
            clock->value->has_next_deadline = true;//可选参数的标记
            clock->value->next_deadline = deadline;
        }
        clock->value->clock_name = g_strdup(name);
        clock->next = clockList;
        clockList = clock;
    }
    return clockList;
}

(3)编译测试

{ "execute": "qmp_capabilities" }
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
{ "execute": "hello-world", "arguments": { "message": "We like qemu" } }

这里写图片描述
9. 使用libvirt的API发送QMP的json格式命令

(1) virsh -c qemu:///system qemu-monitor-command VM_NAME “{ \”execute\”: \”hello-world\”, \”arguments\”: { \”message\”: \”We love qemu\” } }”

(2) virsh -c qemu:///system qemu-monitor-command VM_NAME “{ \”execute\”: \”hello-world\”, \”arguments\”: { \”message\”: \”We like qemu\” } }”

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011414616/article/details/80055615

智能推荐

改善代码可读性的 5 种方法_weixin_38754349的博客-程序员宅基地

code小生一个专注大前端领域的技术平台公众号回复Android加入安卓技术群作者 | Dev by RayRay译者 | 王强策划 | 李俊辰本文最初发布于 byrayray.d...

php mysql 0开头数字,mysql,hibernate_HQL语句关于0开头的数字型字符串的奇怪问题,mysql,hibernate - phpStudy..._weixin_39625008的博客-程序员宅基地

HQL语句关于0开头的数字型字符串的奇怪问题目前在修改维护一个项目,以前系统设计的时候有个表的主键XID设置为数字型的字符串类型,如0000000000,0000000001,查询的时候遇到一个奇怪的问题,有的XID报错,有的XID正常。比如说下面这条语句就能正常执行:select count(entity.id) from XFHiddenTrouble as entity where enti...

datetime 转java_Java 和 Javascript 的 Date 与 .Net 的 DateTime 之间的相互转换_weixin_39639381的博客-程序员宅基地

Java 和 Javascript 的 Date 对象内部存放的是从1970年1月1日0点以来的毫秒值。.Net 的 DateTime 对象内部存放的是从0001年1月1日12点以来的tick值,1ticks=100纳秒=0.1微秒。因此,我们可以借助1970年1月1日0点这个特殊的时刻来对二者进行换算,代码如下:using System;namespace Extends{public stat...

com.android.deskclock意外停止,Permission Denial: opening provider com.android.deskclock.provider.ClockPr..._weixin_39935388的博客-程序员宅基地

在通过读取系统闹铃的provider获取闹铃时间列表时,出现了这个异常:Caused by: java.lang.SecurityException: Permission Denial:opening provider com.android.deskclock.provider.ClockProvider fromProcessRecord{65f49190 8676:com.wisesean...

android自动切换暗色,根据环境光亮度自动切换,让 Android 10 的暗色主题更智能:Auto Dark Theme..._苏远岫的博客-程序员宅基地

从 iOS 13 的暗色模式到 Android 10 的暗色主题,对类似功能的系统级支持是今年的一大热点,也为我们带来了不少便利。不过大部分围绕暗色模式或暗色主题的自动化实现都是与 时间 紧密挂钩的,而在一些特定应用中,你可能还见过这样的暗色主题触发方式:某 IM 应用所以有没有办法让 Android 10 自带的暗色主题也拥有「根据环境光强度自动启用 / 关闭暗色主题」的功能呢?Auto Dar...

matlab求拉斯反变换,MATLAB拉式反变换输出结果有问题_小泽t的博客-程序员宅基地

这是程序代码[/syms Vin Vo Cd Cs Cjp Cj L Lm VCd0 IL0 ILm0 Vop VCjp0 n s;Lf1=(Vin/s+L*IL0+Lm*ILm0-VCd0/s-(Lm*Vop-Lm*VCjp0+s*Lm^2*ILm0)/(s*Lm+1/2/s/Cjp))/(s*L+1/(s*Cs)+1/(s*Cd)+(Lm/2/Cjp)/(s*Lm+1/2/s/Cjp));f1...

随便推点

用c语言求n以内的素数,关于求N以内素数的一点小问题(N小于一亿)_体制内生存之道的博客-程序员宅基地

该楼层疑似违规已被系统折叠隐藏此楼查看此楼我个人觉得第二个可能是因为内存不够的缘故,于是照着网上的办法弄了一个辅助布尔型数组来改进一下,然后就变成这样了……结果是2The total of the primes are: 1代码如下#include#include#define N 10000using namespace std;int main(){ofstream outfile;outf...

java 实验报告模板_java实验报告模板_李肖恩的博客-程序员宅基地

java实验报告模板1 / 26java 实验报告模板河南工业大学实验报告课程 Java 程序设计 _ 实验名称 一、Java 程序流程控制 院 系____ ____ 专业班级__ _________ 姓 名_______________ 学 号____________ _ 指导老师: 日 期一.实验目的熟悉 Java 语言中的数据类型、变量声明、流程控制语句。二.实验要求每个人独立完成程序的调试...

原神如何修改服务器,原神PC端界面太大怎么修改 pc窗口界面调整方法分享[多图]..._IBEANI的博客-程序员宅基地

原神PC端界面太大了,这个是默认的窗口,很多玩家都不太习惯的,如何修改最佳的视觉效果,此次也也是值得大家去研究的,下面就来介绍下pc界面大小怎么改。原神pc版窗口大小调整技巧分享方式一:1、右击启动程序(原神启动器),点击【属性】。2、点击兼容性,再点击【更改高DPI设置】。3、勾选【代替高DPI缩放行为】,缩放执行【系统(增强)】,确定后应用就能解决。方式二:直接右键桌面,进入【显示设置】,在【...

电大远程教育计算机应用基础,(2017年电大)2017年电大远程网络教育计算机应用基础统考模拟试题集.doc..._顾煜的博客-程序员宅基地

(2017年电大)2017年电大远程网络教育计算机应用基础统考模拟试题集2015年电大远程网络教育计算机应用基础统考模拟试题集1、要为所有幻灯片添加编号,下列方法中正确的是_____ 。A 、执行"插入"菜单的"幻灯片编号"命令即可B、在母版视图中,执行"插入"菜单的"幻灯片编号"命令C、执行"视图"菜单的"页眉和页脚"命令D、执行"审阅"菜单的"页眉和页脚"命令标准答案:A2、演示文稿中,超链接...

555定时器组成的应用之流水灯_OMGMac的博客-程序员宅基地_555定时器流水灯

我们都知道555集成电路应用很广泛,并且它是将数电和模电结合的很精妙的一种电路。这次介绍如何用555定时器组成的多谐振荡器和集成电路4017设计流水灯。本实验我们依然采用proteus进行仿真,这里我们先介绍一下本电路分为两部分,分别是前半部分的由555定时器构成的多谐振荡器和后半部分的由4017组成的十进制计数器。555...

MySQL8.0.16详细安装教程--包含卸载教程_心动不能行动!!!的博客-程序员宅基地

安装数次,问题不断,所以打算写下教程。一、卸载如果是重新安装则需要按步骤来,首次安装可跳过。1.控制面板--程序--卸载--MySQL如果有的话,卸载。2.安装目录--删除我的目录为C:\Program Files\mysql-8.0.16.3.注册表中卸载在运行--输入“regedit”--编辑--查找--输入“mysql”--删除4.命令行删除cmd...

推荐文章

热门文章

相关标签