近期学习了一下Qemu的使用,发现Qemu功能很强大,提供的能力较多,同时也为使用增加的难度,尤其是那堆参数。同样是由于那堆参数,可以构造出很复杂的模拟环境来。不过构造模拟环境不是这次的重点,这次重点是如何快速搭建一个可以调试内核的环境来。
此次使用的系统环境是Ubuntu 20.04版本。通过sudo su -切换到root用户上进行操作,从安全角度来说不推荐,可以基于普通用户加sudo去操作需要特权的命令。所以下面的命令都是默认没有带sudo的,根据具体情况自行添加。
- 安装Qemu及编译内核
首先,基于刚安装完毕的Ubuntu系统,我们需要把Qemu安装起来。Qemu的主要安装命令可以用:
apt install qemu qemu-kvm bridge-utils virt-manager
接着,我们需要把编译内核的必备软件给安装起来。
apt install bison flex libelf-dev libssl-dev build-essential libncurses-dev
好了,基本上可以编译内核了。可以通过make menuconfig配置好内核选项,建议把文件系统都编译到内核中,而不是使用模块的方式去编译。主要是避免调试内核的时候,找不到文件系统模块,然后系统挂掉了,没法继续往下跑了。这里我选择了ext4文件系统,因为我将要构造一个ext4的文件系统的环境。
这就是编译好的结果:
- 构造rootfs
我们可以开始构造rootfs,一个让系统能够跑起来所依赖的根文件系统。Qemu不像前面使用VMWare那样,安装系统后,在Guest机器内编译安装内核,然后修改虚拟机参数什么的。Qemu在使用上,这点很好玩。它可以将调试的内核和运行的文件系统分离,这就意味着,构造一个rootfs之后,可以随意使用不同的内核去拉起系统,也就是说可以复用同一个rootfs去调试任意的内核。
首先,我们需要在下面的地址从ubuntu的发布件下载一个base的压缩包。这是一个基础的文件系统目录结构和一些通用配置文件。
http://cdimage.ubuntu.com/cdimage/ubuntu-base/releases/20.04/release/
根据自己需要吧,如果想模拟ARM环境可以下载ubuntu-base-20.04-base-arm64.tar.gz,在此下载了ubuntu-base-20.04-base-amd64.tar.gz压缩包。
接着我们需要使用下面命令创建一个镜像文件:
dd if=/dev/zero of=rootfs.img bs=1024 count=1M
由于还需要安装其他软件,所以就构造了一个1G大小的,当然可以按需构造。反正失败了还可以重来。
创建好空的镜像文件后,我们需要对它进行格式化。
mkfs.ext4 -F -L linuxroot rootfs.img
在此将它格式化为ext4的格式。
再接着我们将该镜像文件作为磁盘挂载起来。先来创建一个挂载的目录:
mkdir /mnt/tmpdir
挂载上去:
mount -o loop rootfs.img /mnt/tmpdir/
紧接着我们将刚刚下载的压缩包ubuntu-base-20.04-base-amd64.tar.gz解压到该镜像文件系统中。使用命令:
tar zxvf ubuntu-base-20.04-base-amd64.tar.gz -C /mnt/tmpdir/
挂载好后,我们需要往它上面安装软件,为了能够使用上apt命令去安装软件,因此需要在chroot切换根环境前,把所需的DNS配置及各种内存文件系统挂载上去。
cp /etc/resolv.conf /mnt/tmpdir/etc/
mount -t proc /proc /mnt/tmpdir/proc
mount -t sysfs /sys /mnt/tmpdir/sys
mount -o bind /dev /mnt/tmpdir/dev
mount -o bind /dev/pts /mnt/tmpdir/dev/pts
挂载好后,我们要切换根文件系统了。
chroot /mnt/tmpdir
切换完成后,现在就在rootfs文件系统中了,可以开始我们的程序安装:
apt-get update
apt-get install \
language-pack-en-base \
sudo \
ssh \
net-tools \
ethtool \
wireless-tools \
ifupdown \
network-manager \
iputils-ping \
rsyslog \
htop \
vim \
xinit xorg \
alsa-utils \
–no-install-recommends
可以根据自己基于上述安装的软件上,增加所需的一些软件。
软件安装完毕后,为了能够登录系统,我们添加一个用户:
useradd jean
这里的jean是我们需要添加的普通用户,可以改成自己的用户名。别忘了还要passwd jean去为这个用户设置密码。当然如果你想要用root登录系统,还需要给root去添加一个root的密码。
设置好用户后,设置hostname信息:
echo “jeanhost” > /etc/hostname
修改/etc/hosts配置路由信息:
127.0.0.1 localhost
127.0.0.1 jeanhost
修改/etc/X11/Xwrapper.config文件,允许所有人使用X用户界面:
allowed_users=anybody
通过以下命令设置时区:
dpkg-reconfigure tzdata
修改/etc/group为声卡驱动添加group用户组权限:
audio:x:29:pulse,username
修改/etc/group为图形驱动添加group用户组权限:
video:x:44:username
至此搞定了基本的rootfs文件系统了。
开始卸载问题系统了:
umount /mnt/tmpdir/proc/
umount /mnt/tmpdir/sys/
umount /mnt/tmpdir/dev/pts/
umount /mnt/tmpdir/dev/
umount /mnt/tmpdir/
- 调试内核
可以开始调试内核了。使用命令:
qemu-system-x86_64 -s -S -m 4096M -kernel linux-5.4.60/vmlinux -append “root=/dev/sda console=ttyS0” -hda rootfs.img
-s是-gdb tcp::1234缩写,监听1234端口,在GDB中可以通过target remote localhost:1234连接;
-S表示加载后立即暂停,等待调试指令。不设置这个选项内核会直接执行;
-kernel指定编译好的调试版内核文件vmlinux;
-had指定构建的rootfs镜像文件;
-append指定内核启动参数。
Kernel是未压缩的vmlinux?是的,没看错,就是这么好玩。
在启动虚拟机后,我们可以通过gdb vmlinux直接调试内核,然后使用命令:
target remote :1234
接入要调试的虚拟机。可以开始正常调试了。