使用 kvmtool 运行和调试 Linux 内核

关注微信公众号:Linux 内核拾遗
一、为什么选择 kvmtool
在虚拟化技术领域,QEMU(Quick Emulator)无疑是应用最广泛的全能型选手。这个开源机器仿真器能够:
支持 x86、ARM、RISC-V 等 20+处理器架构。
提供完整的设备模拟(网卡、显卡、声卡等)。
兼容多种虚拟化方案(KVM、Xen、TCG 等)。
实现复杂的功能特性(实时迁移、快照管理)。
但正是这种全能性使得 QEMU 的代码规模超过 150 万行,学习其源码如同大海捞针。当我们的目标是专注学习 KVM 虚拟化技术的核心原理时,QEMU 的复杂性反而成为了认知负担:

KVMtool(全称 Linux Kernel Virtual Machine Tool)则采取了截然不同的设计思路:
其核心特点包括:
代码精简:约 1 万行 C 代码(仅为 QEMU 的 0.6%)。
功能聚焦:仅使用 KVM 进行硬件辅助虚拟化。
零抽象层:直接操作虚拟化扩展指令集。
开发友好:代码逻辑线性可追踪。
如果只是为了学习 kvm 虚拟化技术,那么就可以尽量简化用户态管理程序。这里以开源的工具 kvmtool 为例讲讲 kvm 是如何运行和调试我们的 Linux 系统的。
二、环境准备
由于 kvmtool 底层使用的是 kvm,因此我们在实践之前,需要准备一台运行 Linux 内核、并且带有 kvm 子系统的主机。目前主流的 Linux 发行版都能满足要求,可以通过lsmod | grep kvm
命令来检查:

如果你是在 Windows 或者 macOS 环境中安装 Linux 虚拟机,请确保处理器硬件支持嵌套虚拟化,并且在 VMWare、Parallel Desktop 等虚拟机软件中为虚拟机开启嵌套虚拟化能力。这里不再深入展开,如果恰好你是这种情况,麻烦各位自行采取各种手段解决吧。
三、源码获取与编译
1. kvmtool 编译和安装
可以直接从 Github 上获取 kvmtool 源代码:
kvmtool 的编译很简单,直接make
即可,然后使用make install
进行安装:
安装完成后,可以通过lkvm
来使用 kvmtool 了:
2. Linux 系统准备
为了运行 Linux 系统,我们需要准备”Linux 内核映像“和”根文件系统映像“这两个东西。
具体过程这里不再详细阐述,大家可以参考:https://mp.weixin.qq.com/s/y27zD6DPYstdVSoHwkOMsg。
最后只需要两个文件:
Linux 内核映像:
linux/arch/x86/boot/bzImage
。根文件系统映像:
initramfs.img
。
四、使用 kvmtool 启动虚拟机
kvmtool 启动虚拟机的方式和 QEMU 很类似,基本就是指定内核文件、根文件系统,然后配置核心数、内存大小以及内核启动参数等:
可以看到,Linux 内核正常跑起来了:

同样也支持基本的 BusyBox 命令:

1. kvmtool 常用命令
kvmtool 允许用户对虚拟机执行一些基本操作,例如查看当前虚拟机状态、暂停或者恢复虚拟机执行等:

2. GDB 调试虚拟机
kvmtool 支持通过 gdb 来调试我们的虚拟机,其底层利用的是 Linux 内核的 kgdb 调试机制。
这里先贴一下 kvmtool 官方提供的调试步骤:

但是我在实践过程中没有办法直接复现,下面就介绍一下我自己摸索过后切实可行的步骤。
首先我们需要重新编译我们的 Linux 内核映像,编辑.config
文件如下:
上面的配置只启用了 KGDB,没有使用 KDB,原因后面说明。
重新编译内核,得到arch/x86/boot/bzImage
。
然后 kvmtool 启动虚拟机,并且添加 kgdb 相关启动参数:
终端输出内核日志如下:


日志中Info: Assigned terminal 1 to pty /dev/pts/1
意思是说虚拟机的串口 1ttyS1
绑定到了宿主机上的伪终端/dev/pts/1
。
我们可以另起一个宿主机终端,通过 gdb 来连接到调试串口/dev/pts/1
:
可以看到虚拟机执行暂停在了 kgdb 断点处:

接下来我们就可以跟调试普通程序一样,使用常用的 gdb 命令来调试 Linux 内核了。对于需要修改内核代码,或者开发驱动模块,使用 kgdb 调试是非常有用的。
3. 使用 KDB?
上一节提到过,我们重新编译 Linux 内核前将 KDB 模式给关闭了,原因是我当时在开启 KDB 的情况下,gdb 无论如何也连接不上去,从 kdb 也没法切换到 kgdb 模式,遂放弃了 Orz。。。
开启 KDB 模式的情况下,虚拟机启动后自动进入了 KDB shell:

Linux 内核官方文档中指出,有两种方式从 KDB 模式切换到 KGDB 模式:

一种方式是通过 gdb 连接到调试端口后 kdb shell 会自动识别到并切换到 kgdb 模式,另一种是直接在 kdb shell 中直接执行kgdb
命令来手动切换。
但是我两种方式尝试下来都不行。
下面是 gdb 连接报错:


然后尝试直接进入 kdb shell,手动切换 kgdb:

有哪位大佬知道是什么原因吗?欢迎私信或者留言探讨交流,非非非常感谢!
关注微信公众号:Linux 内核拾遗
版权声明: 本文为 InfoQ 作者【Linux内核拾遗】的原创文章。
原文链接:【http://xie.infoq.cn/article/fe4bd674ba4b2210239c2a0f2】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论