在调试内核我们使用 initramfs + kernel 构建了一个基础的 linux kernel 调试环境, 但是目前的所有操作都只会保存在 initramfs 中, 目前可用的软件很少, 没有编译器, 没有联网, 没有 apt 包管理工具, 不能持久化存储
本文介绍如何使用 qemu 模拟更加复杂的环境, 以及如何利用现有的 Linux 发行版根文件系统(比如说 Ubuntu 的基础软件包)来搭建一个可用的 linux 开发环境, 替换自己编译的内核
手动构建软件包环境是十分繁琐的, 可以直接利用现有的 Linux 发行版提供的系统映像安装, 这里以 Ubuntu 为例
可以在 ubuntu server 查找到 Ubuntu 的 server 镜像 iso 文件 也有一些其他存档镜像:
下载的 iso 是光盘 CD/DVD 映像, 需要先创建一个存储介质用于安装系统
qemu-img create ubuntu.raw -f raw 20G
安装系统, 其中 -drive
-cdrom
的路径自行调整
qemu-system-x86_64 \
-m 4G \
-drive format=raw,file=disk/ubuntu.raw,if=virtio \
-cdrom iso/ubuntu-24.04-live-server-amd64.iso
具体的安装过程略过, 注意不要开启 LVM, 启用 OpenSSH
安装完成之后就不再需要 -cdrom
了, 可以直接通过磁盘启动
qemu-system-x86_64 \
-m 4G \
-drive format=raw,file=disk/ubuntu.raw,if=virtio
启动会有点慢, 耐心一些
启动之后可以使用 lsblk 看一下当前的所有磁盘, 启动 sr0 代表第一个光盘驱动器(CD-ROM Drive), vda 代表第一个 Virtio 磁盘设备
kamilu@kamilu:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sr0 11:0 1 1024M 0 rom
vda 253:0 0 20G 0 disk
├─vda1 253:1 0 1M 0 part
└─vda2 253:2 0 20G 0 part /
由于命令行中设置参数中 if(interface) 指定为
if=virtio
所以这里显示的是 vda, 如果设置为if=sd
则这里应该是 sda, 这里设置为 virtio 可以让设备通过允许客体系统直接与主机通信而绕过仿真层, 以实现高效的磁盘Iqemu 全部支持的设备模拟见下文 qemu 参数
可以看到 /dev/vda1
是用于启动的 /boot 分区, /dev/vda2
是挂载的根分区, 也是后面启动时需要手动指定的分区
使用 Linux 发行版镜像的主要目的就是借用其已经编译好的软件包环境, 通常内核则使用自己修改后重新编译的 bzImage
qemu-system-x86_64 \
-m 4G \
-kernel bzImage \
-drive format=raw,file=disk/ubuntu.raw,if=virtio \
-append "root=/dev/vda2 console=ttyS0" \
-nographic -no-reboot -d guest_errors
-d guest_errors
是 QEMU 的一个调试选项,用于记录和报告客户机操作系统中的错误, 这些错误信息会被输出到 QEMU 的日志中,以便开发人员或系统管理员进行调试和问题排查.
如果设置了 kernel 参数那么就会使用新内核而不是当前 ubuntu.raw 中的内核, 相当于不再使用 /dev/vda1
的 /boot 分区, 不会读取其中 /boot/grub/grub.cfg
的配置文件, 因此需要手动指定挂载的根分区所在的位置
启动的时候可能会出现如下报错: VFS: Unable to mount root fs on unknown wn-block(0,0)
可能有两个原因, 一个是因为现在使用的是 qemu 的虚拟机环境, 所以需要内核也支持虚拟化的设备/网络/PCI模拟, 需要在内核选项中开启如下的配置
CONFIG_EXT4_FS=y
CONFIG_XFS_FS=y
CONFIG_JBD2=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_NET=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_RING=y
正常来说使用
defconfig
的应该都已经开启了, 如果没有可以在 menuconfig 中手动搜索一下开启
这一步我们选择一个比较老的 Ubuntu 18.04 mini.iso, 只有 70MB 大小, 其余软件是从网络上下载的
这个版本的镜像没有安装 openssh, 需要先安装
sudo apt install net-tools
sudo apt-get install openssh-client
sudo apt-get install openssh-server
sudo /etc/init.d/ssh restart
最后检查一下防火墙是不是关闭了
sudo ufw status
# inactive 表示关闭
接下来就可以使用 ssh 从外部连接了
.gdbinit
可以完成一些提前需要处理的操作, 比如说可以手动加载一个可执行文件
set confirm off
dir /home/kamilu/linux/linux-4.4.6
add-symbol-file /home/kamilu/linux/linux-4.4.6/abc 0x080490a0
b abc.c:main
add-symbol-file 用于添加一个符号
readelf -S abc | grep .text
在 QEMU 中,-drive if=interface
参数用于指定驱动器的接口类型.以下是每种接口类型的解释及其区别:
if=ide
):if=scsi
):if=sd
):if=mtd
):if=floppy
):if=pflash
):if=virtio
):这些接口类型决定了虚拟驱动器在客体操作系统中的表现形式,并会影响性能、兼容性和可用特性.
在 QEMU 中,fd0
, sr0
, vda
, hda
, sda
等设备名称有特定的含义和用途,下面是这些设备名称的基本解释:
fd0
通常是软盘映像文件的设备名称.sr0
通常是光盘映像文件的设备名称.vda
是第一个 Virtio 磁盘设备,vdb
是第二个 Virtio 磁盘设备.hda
是第一个 IDE 磁盘设备,hdb
是第二个 IDE 磁盘设备.sda
是第一个 SCSI 磁盘设备,sdb
是第二个 SCSI 磁盘设备.在 QEMU 的命令行或配置文件中,你可能会看到类似以下的配置:
-drive file=/path/to/floppy.img,format=raw,if=floppy
-cdrom /path/to/cdrom.iso
-drive file=/path/to/disk.img,format=raw,if=virtio
-drive file=/path/to/disk.img,format=raw,if=ide
-drive file=/path/to/disk.img,format=raw,if=scsi
下面是一个使用 QEMU 启动虚拟机并配置不同设备的完整示例:
qemu-system-x86_64 \
-m 512M \
-smp 2 \
-hda /path/to/hda.img \
-drive file=/path/to/disk.img,format=raw,if=virtio \
-cdrom /path/to/cdrom.iso \
-drive file=/path/to/floppy.img,format=raw,if=floppy \
-net nic -net user
在这个例子中:
-m 512M
设置内存为 512MB.-smp 2
设置虚拟机有两个 CPU 核心.-hda /path/to/hda.img
指定第一个硬盘驱动器(IDE).-drive file=/path/to/disk.img,format=raw,if=virtio
指定一个 Virtio 磁盘驱动器.-cdrom /path/to/cdrom.iso
连接一个 CD-ROM.-drive file=/path/to/floppy.img,format=raw,if=floppy
连接一个软盘驱动器.通过这些设备名称和配置,QEMU 可以模拟各种硬件环境,适用于不同的虚拟化需求.
在 QEMU 中,-drive
和 -hda
都用于指定虚拟机的硬盘驱动器,但是它们有一些重要的区别.
-hda
-hda
是一个较为简单和旧的方式来指定硬盘镜像文件.它有几个局限性,例如:
示例用法:
qemu-system-x86_64 -hda /path/to/disk_image.img
在这个例子中,-hda
选项指定了一个硬盘镜像文件.
-drive
-drive
是一种更灵活和现代的方式,提供了更多的配置选项,可以更细致地控制硬盘驱动器的行为.它支持多个参数,允许用户指定驱动器类型、接口类型、缓存策略等.
示例用法:
qemu-system-x86_64 -drive file=/path/to/disk_image.img,if=virtio,cache=writeback
在这个例子中:
file=/path/to/disk_image.img
指定硬盘镜像文件.if=virtio
指定接口类型为 virtio,这通常比默认的 IDE 更高效.cache=writeback
设置缓存策略为写回.-drive
参数示例file
:硬盘镜像文件路径.if
:接口类型,可以是 ide
、scsi
、virtio
、none
等.index
:指定驱动器索引.media
:指定介质类型,可以是 disk
或 cdrom
.cache
:指定缓存策略,可以是 none
、writeback
、writethrough
等.format
:指定镜像文件格式,如 raw
、qcow2
.-hda
简单直接,但灵活性差,适用于简单场景.-drive
功能强大,灵活性高,适用于需要详细配置的复杂场景.使用 -drive
可以更精确地控制虚拟机的硬盘行为和性能,因此在复杂的虚拟化环境中,通常建议使用 -drive
选项.
-boot d
-hda
-cdrom
noapic
是传递给内核的一个启动参数,用于禁用 APIC(Advanced Programmable Interrupt Controller), 对于某些老旧的操作系统或不完全支持 APIC 的系统,禁用 APIC 可能提高兼容性和稳定性
-machine accel=kvm:tcg