编辑
2025-11-25
系统知识
0

目录

安装QEMU
编译内核
准备根文件系统
开始调试内核

本文将在Ubuntu22.04下搭建一个QEMU的运行环境,并编译Linux内核源码,随后通过QEMU来调试Linux内核,用以辅助Linux内核的学习。

事实上,我在Ubuntu22.04Ubuntu24.04下都试过了,流程上是一样的,但是Ubuntu24.04因为内核版本太新(至少对于在写这篇文章的时间点来说),踩了一些细节上的坑,最终调试效果并不好,所以我还是建议选用Ubuntu22.04

安装QEMU

我采用的是从源码编译再安装的方式,这个方法具备一般性。

首先去官网下载QEMU的源码,我使用的是10.1.2版本的,你可以根据需要选择合适的版本:

https://www.qemu.org/download/

随后查看官方的编译环境配置手册,根据具体系统来进行具体配置:

https://www.qemu.org/docs/master/devel/build-environment.html

我使用的是Ubuntu,根据官方手册内容,可以使用命令:

shell
sudo apt update && sudo apt build-dep qemu

然后进入QEMU源码根目录,创建编译目录并开始编译:

shell
mkdir build cd build ../configure # 这里我会开启并行编译加快编译速度 # 不需要并行编译可以直接make make -j`nproc`

编译完成后可以安装到系统目录:

shell
sudo make install

随后可以随便选择一个架构来看看编译安装的情况如何,正常情况下以下命令会输出一些帮助信息:

shell
qemu-system-x86_64 -h

编译内核

先安装一个库:

shell
sudo apt install -y libelf-dev

随后下载Linux内核源码,选择你需要的版本:

https://cdn.kernel.org/pub/linux/kernel/

我选择的版本是6.8.12,使用如下命令下载并解压,再进入内核源码根目录:

shell
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.12.tar.xz tar -xf linux-6.8.12.tar.xz cd linux-6.8.12

Ubuntu22.04的内核大版本也是6.8,而Ubuntu24.04的内核大版本是6.14,这一点你可以通过命令uname -r来确定。

这可能会对内核的编译带来影响,因为GCC编译器也是跟着系统版本来的,至少对于通过apt安装的是这样。

然后使用默认配置:

shell
make defconfig

如果你需要调整内核的配置,可以换用以下命令:

shell
make menuconfig

然后就可以开始编译内核了:

shell
make -j`nproc` vmlinux bzImage

编译成功后,得到两个关键的文件:

# 位于内核源码根目录 vmlinux # 位于./arch/x86/boot bzImage

准备根文件系统

构建根文件系统的方式还挺多的,我们也不需要太复杂的根文件系统,所以我打算采用Busybox+手动构建,类似的选择还有BuildrootYocto,但这两个配置上复杂一些,不过好处就是不用手动创建一些文件。

构建根文件系统的方式不唯一,你可以根据你的知识积累做这一步,用其他的方式构建可能需要你去更改内核配置(使用命令make menuconfig),打开initramfs的支持,在我这默认是打开了的:

General setup ---> [*]Initial RAM filesystem and RAM disk(initramfs/initrd)

去官网下载Busybox

https://busybox.net/downloads/

我选择的版本是1.37.0

随后解压并进入其根目录:

shell
tar -xf busybox-1.37.0.tar.bz2 cd busybox-1.37.0

需要调整Busybox的配置:

shell
make menuconfig

随后会打开一个界面,如下图所示:

image.png

我们选择以下选项:

Settings ---> [*] Build static binary (no shared libs)

image.png

配置完成保存退出即可,最简单的方式是疯狂按Esc键,直到弹出询问你是否保存的界面,选择保存即可。

image.png

然后开始编译根文件系统:

shell
make

这里有个踩坑提示,在Ubuntu24.04下会编译失败,Ubuntu22.04是没有这个问题的,主要还是因为Ubuntu24.04用的内核版本太新了(可以通过命令uname -r来查看)。

image.png

原因如下:

https://github.com/gramineproject/gramine/issues/1909

解决方法可以查看以下链接:

https://lists.busybox.net/pipermail/busybox-cvs/2024-January/041752.html

我的做法是在根目录下编辑配置文件,将CONFIG_TC这个选项关掉:

shell
vi .config

image.png

然后清理一下之前的编译产物并重新编译即可:

shell
make clean && make

编译完成之后,打包一下:

shell
make install

打包路径为当前目录下的_install文件夹

shell
cd _install ls

image.png

该目录将成为我们制作的根文件系统,从当前该目录所罗列的文件可以看出,少了些东西,我们现在需要手动构建。

shell
mkdir proc mkdir sys # 创建并编辑文件init,复制下面给出的内容即可 vi init chmod +x init
init的内容
#!/bin/sh mkdir /tmp mount -t proc none /proc mount -t sysfs none /sys mount -t debugfs none /sys/kernel/debug mount -t tmpfs none /tmp mdev -s setsid /bin/cttyhack setuidgid 1000 /bin/sh

接下来打包根文件系统到HOME目录:

shell
find . | cpio -o --format=newc > ~/rootfs.img

开始调试内核

确定我们准备好了以下三个文件:

image.png

打开两个终端,一个终端运行QEMU,另一个终端运行GDB

shell
# 终端A qemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs.img -append "nokaslr console=ttyS0" -s -S -nographic # 终端B gdb ./vmlinux (gdb) target remote :1234 (gdb) b start_kernel (gdb) c

我们给内核的start_kernel函数下了断点,并开始执行内核,现在会在内核跑到start_kernel函数时暂停:

image.png

这样大体上就成功了,可以开始用GDB调试Linux内核源码了,可以给GDB输入命令c让它继续加载进入系统。

本文作者:Test

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!