2. 使用QEMU监视器控制虚拟机
任务说明
本案例目标是通过QEMU监视器(QEMU Monitor)对运行中的虚拟机执行基本控制操作,包括查看虚拟机运行状态、发送键盘按键事件等。你将启动一个最小化的QEMU虚拟机实例,并通过标准输入/输出或Unix Socket方式接入监视器,验证命令执行效果。
实现方案
QEMU监视器提供了一套交互式命令接口,支持info、sendkey、quit等操作。本案例使用-monitor stdio将监视器绑定到当前终端,同时使用-display none和-serial none避免图形窗口与串口干扰,使监视器成为唯一交互通道。虚拟机使用QEMU自带的isa-debug-exit设备,该设备在收到特定I/O写入后会使QEMU以指定退出码退出,便于验证sendkey等命令是否真正送达虚拟机内部。
代码实现
创建以下启动脚本start_vm.sh:
#!/bin/bash
# 启动一个带监视器的最小QEMU虚拟机
# 使用isa-debug-exit设备:向I/O端口0x501写入值N,QEMU以退出码(N<<1)|1退出
exec qemu-system-x86_64 \
-M pc \
-m 64M \
-display none \
-serial none \
-monitor stdio \
-device isa-debug-exit \
-nodefaults
赋予执行权限并启动:
chmod +x start_vm.sh
./start_vm.sh
启动后终端进入QEMU监视器交互界面,提示符为(qemu)。
监视器命令操作
在(qemu)提示符后依次输入以下命令并观察输出:
(qemu) info status
# 预期输出: VM status: running
(qemu) info registers
# 预期输出: 显示CPU寄存器快照,RAX=0x0000000000000000 等
(qemu) sendkey ctrl-alt-f1
# 预期输出: (无输出,按键已注入虚拟机)
(qemu) info mice
# 预期输出: 显示当前鼠标设备列表
(qemu) quit
# 预期输出: QEMU进程正常退出,返回终端shell
验证sendkey命令确实生效
仅执行sendkey而无外部观察手段时,无法确认按键是否真正注入。为验证sendkey的实效性,利用isa-debug-exit设备特性:该设备监听I/O端口0x501,当收到写入值N时,QEMU以退出码(N<<1)|1终止。由于虚拟机内无操作系统处理按键,需借助QEMU的sendkey配合isa-debug-exit的I/O端口触发机制——但该设备默认不响应键盘。因此,更直接的验证方式是使用监视器的sendkey向虚拟机发送ctrl-alt-delete,并观察QEMU日志或使用-d选项跟踪中断注入:
# 重新启动,开启中断日志
qemu-system-x86_64 -M pc -m 64M -display none -serial none \
-monitor stdio -device isa-debug-exit -nodefaults \
-d int -D /tmp/qemu_int.log
# 在监视器中执行:
(qemu) sendkey ctrl-alt-delete
(qemu) quit
# 检查日志中是否出现键盘控制器中断注入记录
grep -i "kbd\|keyboard\|8042" /tmp/qemu_int.log
若日志中包含键盘控制器(8042)相关中断注入条目,则证明sendkey命令已成功将按键事件送入虚拟机硬件层。
验证方法与判定标准
| 验证项 | 操作 | 通过标准 |
|---|---|---|
| 监视器可用 | 启动后输入info status | 输出VM status: running |
| 状态查看 | 输入info registers | 显示寄存器列表且无错误提示 |
| 按键注入 | 输入sendkey ctrl-alt-f1 | 命令无报错,QEMU不退出 |
| 按键实际送达 | 开启-d int日志后执行sendkey ctrl-alt-delete | 日志中出现键盘控制器中断记录 |
| 退出监视器 | 输入quit | QEMU进程正常结束,shell提示符返回 |
监视器命令交互流程
关键说明
-nodefaults参数移除了默认设备,确保虚拟机环境最小化,避免默认VGA、键盘等设备干扰监视器行为。isa-debug-exit设备在本案例中作为虚拟机内可观测的退出机制,但验证sendkey时需依赖-d int日志,因为该设备不直接响应键盘输入。- 若需在无图形环境下验证按键对客户机操作系统的实际影响,可加载一个极小的内核(如
-kernel参数指定编译好的测试内核),但本案例聚焦监视器命令本身,故采用日志验证方式。