使用 monitor command 监控 QEMU 运行状态

在虚拟化的研究领域,QEMU 有着举足轻重的地位。2007 年 2 月发布的 Linux 2.6.20 内核中,集成了 KVM 作为其虚拟化的具体实现。而 KVM 是基于 QEMU 并且利用 CPU 的辅助虚拟化特性而略加修改而成的。自此以后,QEMU 项目引起 Linux 开发人员的广泛关注。

进入 QEMU monitor

在启动 QEMU 的时候,同时也会启动 monitor 的控制台,通过这个控制台,可以与 QEMU 或者运行状态的虚拟机进行交互。虽然现在有诸如 virt-manager 之类的图形界面的虚拟机管理工具,但是在 monitor 的控制台窗口输入命令似乎更符合 Linux 程序员的开发习惯,而且还能完成一些图形化管理工具所不具备的功能。在 monitor 控制台中,可以完成很多常规操作,比如添加删除设备、虚拟机音视频截取、获取虚拟机运行状态、更改虚拟机运行时配置等等。

事实上,启动 QEMU 后通常是看不到 monitor 界面的。要进入该界面,可以在 QEMU 窗口激活的时候按住 Ctrl+Alt+2 进入,切换回工作界面需要按 Ctrl+Alt+1。另外,还可以在 QEMU 启动的时候指定 -monitor 参数。比如 -monitor stdio 将允许使用标准输入作为 monitor 命令源。这种方式和常见的 Linux 交互式的用户程序无异,所以在做测试工作的时候,可以很方便的编写出对虚拟机监控的 shell 脚本程序。


清单 1. 使用标准输入作为 QEMU monitor 命令源

[root@kvm]# qemu -hda rhel61-oc.img -m 1024 --enable-kvm -vnc :51 -monitor stdio 
 Using CPU model "cpu64-rhel6"
 QEMU 0.12.1 monitor - type 'help' for more information 
 (qemu)


使用 QEMU monitor 命令

QEMU monitor 的命令非常繁杂,并且由于 QEMU 是广受关注的开源项目,代码更新也特别迅速。同样,monitor 命令也会时常得到更新和升级。概括的说,命令可以分为如下几个部分:

辅助类命令

有一部分命令可以称为辅助性命令,比如 info 和 help。help 可以查询显示某个命令的简要帮助信息;info 命令主要用来显示虚拟机的运行信息。比如 info blockstats 将显示虚拟机中的块设备的读写操作的信息:读入字节、写入字节、读写操作的次数等。


清单 2. Info 命令显示块设备状态信息

(qemu) info blockstats 
 ide0-hd0: rd_bytes=42276864 wr_bytes=828416 rd_operations=2352 wr_operations=119 
 ide1-cd0: rd_bytes=0 wr_bytes=0 rd_operations=0 wr_operations=0 
 floppy0: rd_bytes=0 wr_bytes=0 rd_operations=0 wr_operations=0



清单 3. Help 命令显示 info 命令的帮助信息

(qemu) help savevm 
 savevm [tag|id] -- save a VM snapshot. 
                   If no tag or id are provided, a new snapshot is created


调试类命令

在某些场景下,需要远程调试 QEMU。正常情况下需要被调试端安装有 gdbserver 程序,否则将无法进行远程调试。而在 QEMU 的 monitor 中集成了 gdbserver,就解决了这个问题。内置的 gdbserver 允许被调试端不安装 gdbserver 就能进行远程调试。清单 4 中在 tcp 端口 12345 开启了 gdbserver 的远程调试伺服程序。


清单 4. gdbserver 开启调试端口

(qemu) gdbserver tcp::12345 
 Waiting for gdb connection on device 'tcp::12345'


在客户机,就可以通过 gdb 连接到 gdbserver 的监听端口进行调试工作。与常规的本地程序调试程序相似,清单 5 中显示了运行中的 qemu 的堆栈信息。


清单 5. 从客户端连接 gdbserver 进行调试工作

$:~/work/qemu$ gdb qemu -q 
 Reading symbols from /home/wdongxu/work/qemu/qemu...done. 
 (gdb) target remote localhost:12345 
 Remote debugging using localhost:12345 
 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. 
 Loaded symbols for /lib/ld-linux.so.2 
 0xc012c21a in ?? () from /lib/ld-linux.so.2 
 (gdb) break qcow2_co_readv 
 Breakpoint 1 at 0x8077f1a: file block/qcow2.c, line 381. 
 (gdb) bt 
 #0  0x0018846b in ?? () 
 #1  0x0032d067 in ?? () 
 #2  0x0032d9d5 in ?? () 
 #3  0x0032c729 in ?? () 
 #4  0x0032d334 in ?? () 
 #5  0x0037006c in ?? () 
 #6  0x08123278 in configure_rtc_date_offset ( 
    startdate=0x81267af "\213FH\211\a\213FD\211G\004\215\203\254q", 
    legacy=-1080558488) at /home/wdongxu/work/qemu/vl.c:514 
 #7  0x0809cefa in bt_submit_hci (info=0xf2f1f0, 
    data=0x3 <Address 0x3 out of bounds>, length=163370640) 
    at /home/wdongxu/work/qemu/hw/bt-hci.c:1689 
 #8  0x0809d477 in bt_submit_hci (info=0xbf97fd44, 
    data=0x438001 "_alloc_failed_handler", length=18894760) 
    at /home/wdongxu/work/qemu/hw/bt-hci.c:1837 
 #9  0x081b6303 in cirrus_bitblt_rop_fwd_transp_notdst_16 (s=0x809d3b0, 
 ---Type <return> to continue, or q <return> to quit---


x/xp 用法基本上同 gdb 中的 x 相似:

  • x 用来打印虚地址的值。
  • xp 用来打印从某实地址开始的特定格式的值,比如类似于 x 命令使用的 10 进制、16 进制,甚至能够以 i 作为格式打印指令序列。

清单 5 将打印 0x08123278 起始的一个字节,然后打印了 eip 寄存器所示地址开始的三条指令。


清单 6. 从客户端连接 gdbserver 进行调试工作

(qemu) x/b 0x08123278 
 0000000008123278: 0x8b 
 (qemu) xp /3i $eip 
 0x000000000018846b:  les    0x208039c0(%ebx),%ecx 
 0x0000000000188471:  add    (%eax),%al 
 0x0000000000188473:  add    %cl,(%edi)


print 可以进行数学运算,也可以引用某寄存器的值,类似于 gdb 中的 print 命令。如清单 6 所示:


清单 7. print 进行数学运算

(qemu) print 1 + 3 
 4 
 (qemu) print $eax 
 3079397632 
 (qemu) print $eax + 2 
 3079397634


在调试 QEMU 的时候,可能还会需要鉴别某一块内存区域是否正确,这就需要 sum 命令来计算某一个内存区域的校验和,也可以将内存转储到文件供日后分析。


清单 8. 内存转储

(qemu) x/10x 0x0809d477 
 000000000809d477: 0x8b 0x55 0xc8 0x8b 0x4d 0xc4 0x80 0x7d 
 000000000809d47f: 0xd0 0x00 
 (qemu) memsave 0x0809d477 10 mem.log


清单 7 打印了一块内存,并且将 10 个字节的内存转储到 mem.log 文件。清单 8 中用 hexdump 来校验转储内存数据的正确性。


清单 9. 校验内存转储

$:~/work/qemu$ hexdump -C mem.log 
 00000000  8b 55 c8 8b 4d c4 80 7d  d0 00                    |.U..M..}..| 
 0000000a 
控制类命令


控制类命令操作虚拟机、虚拟磁盘和 qemu。

QEMU 运行的时候,如果指定 -snapshot 参数,则会允许虚拟机在运行的时候创建快照 (snapshot,类似于 Windows XP 中的一个系统还原点 )。在清单 9 中,首先用 info snapshots 查看当前已经存在的快照信息,然后用 savevm 命令创建了一个名为 save_name_1 的快照。再使系统还原到 vm-20111025134936 快照时的状态,最后删除了名为 save_name_1 的快照。


清单 10. snapshot 操作

(qemu) info snapshots 
 ID        TAG                 VM SIZE                DATE       VM CLOCK 
 1         vm-20111025134936      202M 2011-10-25 13:49:36   00:02:19.524 
 (qemu) savevm save_name_1 
 (qemu) info snapshots 
 ID        TAG                 VM SIZE                DATE       VM CLOCK 
 1         vm-20111025134936      202M 2011-10-25 13:49:36   00:02:19.524 
 2         save_name_1            202M 2011-10-25 13:51:53   00:04:28.395 
 (qemu) loadvm vm-20111025134936 
 (qemu) delvm save_name_1 
 (qemu) info snapshots 
 ID        TAG                 VM SIZE                DATE       VM CLOCK 
 1         vm-20111025134936      202M 2011-10-25 13:49:36   00:02:19.524 
 (qemu)


由于块设备允许使用缓存,所以会有在虚拟机运行的时候可能会有某些写操作未实际写到设备上。commit 命令将对块设备执行强制刷新操作。对于仍然在缓存中的数据,将会立即写入到块设备上。

对于虚拟机的开关控制,system_reset/system_powerdown 则相当于在电脑上的 reset 和 powerdown 按钮,将强制虚拟机进行重启和关机操作。stop/cont 将使得虚拟机进入 / 退出挂起状态。而 quit 则将直接退出 qemu.

设备类命令

change 命令用的比较广泛。能在虚拟机运行的时候动态更改虚拟机的配置。清单 10 中首先将启动 QEMU 时指定的 vnc :1 端口更改为 :2,然后将 ubuntu-11.04-desktop-i386.iso 作为虚拟 CD 插入到 ide1-cd0 中,最后将其弹出。我们可以通过 info block 来查看每步块设备的变化情况。


清单 11. change 更改系统配置

(qemu) change vnc :2 
 (qemu) change ide1-cd0 /home/public/ubuntu-11.04-desktop-i386.iso 
 (qemu) info block 
 virtio0: removable=0 io-status=ok file=/tmp/vl.KsIMFQ \
  backing_file=/home/public/ubuntu.img ro=0 drv=qcow2 encrypted=0 
 ide1-cd0: removable=1 locked=0 tray-open=0 io-status=ok \
 file=/home/public/ubuntu-11.04-desktop-i386.iso ro=0 drv=raw encrypted=0 
 floppy0: removable=1 locked=0 tray-open=0 [not inserted] 
 sd0: removable=1 locked=0 tray-open=0 [not inserted] 
 (qemu) eject ide1-cd0 
 (qemu) info block 
 virtio0: removable=0 io-status=ok file=/tmp/vl.KsIMFQ \
 backing_file=/home/public/ubuntu.img ro=0 drv=qcow2 encrypted=0 
 ide1-cd0: removable=1 locked=0 tray-open=1 io-status=ok [not inserted] 
 floppy0: removable=1 locked=0 tray-open=0 [not inserted] 
 sd0: removable=1 locked=0 tray-open=0 [not inserted]


可能是由于兼容性问题,QEMU 在鼠标的虚拟化方面做的并不是十分完美。经常有用户抱怨客户操作系统的光标和宿主光标不同步的问题。如果熟悉 monitor 命令,可以变通的解决这一问题。清单 11 将鼠标移动到 (500,500) 的位置,然后虚拟机中的鼠标按下了左键,最后向虚拟机发送了 Ctrl+Alt+Del。


清单 12. 鼠标和键盘操作

(qemu) mouse_move 500 500 
 (qemu) mouse_button 1 
 (qemu) sendkey ctrl-alt-del


小结

本文介绍了 QEMU monitor 的常用命令,并且大部分给出了运行的实例,希望对使用 QEMU 进行开发和测试的读者有所帮助。当然 monitor 的命令还有很多,比如做迁移测试的时候可能会用到 migrate 系列的命令,需要读者根据 QEMU 的进化演变逐渐地学习和体会。