开发环境搭建

调试内核我们使用 initramfs + kernel 构建了一个基础的 linux kernel 调试环境, 但是目前的所有操作都只会保存在 initramfs 中, 目前可用的软件很少, 没有编译器, 没有联网, 没有 apt 包管理工具, 不能持久化存储

本文介绍如何使用 qemu 模拟更加复杂的环境, 以及如何利用现有的 Linux 发行版根文件系统(比如说 Ubuntu 的基础软件包)来搭建一个可用的 linux 开发环境, 替换自己编译的内核

Ubuntu 镜像

手动构建软件包环境是十分繁琐的, 可以直接利用现有的 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 可以让设备通过允许客体系统直接与主机通信而绕过仿真层, 以实现高效的磁盘I

qemu 全部支持的设备模拟见下文 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

在 QEMU 中,-drive if=interface 参数用于指定驱动器的接口类型.以下是每种接口类型的解释及其区别:

  1. IDE (if=ide):
    • 模拟传统的集成驱动电子设备(IDE)控制器.
    • 通常用于旧式PC中的硬盘和CD-ROM.
    • 是较老系统的常用接口.
  1. SCSI (if=scsi):
    • 模拟小型计算机系统接口(SCSI)控制器.
    • 适用于需要高性能磁盘访问的系统.
    • 相较于IDE支持更多类型的设备.
  1. SD (if=sd):
    • 模拟安全数字(SD)卡.
    • 用于模拟可移动的SD卡存储,常见于移动设备和嵌入式系统.
  1. MTD (if=mtd):
    • 模拟存储技术设备(MTD)接口.
    • 用于闪存设备.
    • 适用于使用NOR或NAND闪存的嵌入式系统.
  1. Floppy (if=floppy):
    • 模拟软盘驱动器.
    • 用于需要软盘支持的旧系统.
  1. Parallel Flash (if=pflash):
    • 模拟并行闪存设备.
    • 用于需要闪存存储固件或其他存储需求的系统.
  1. Virtio (if=virtio):
    • 模拟一个准虚拟化接口.
    • 通过允许客体系统直接与主机通信而绕过仿真层,提供高性能.
    • 常用于虚拟化环境中,以实现高效的磁盘I/O.

这些接口类型决定了虚拟驱动器在客体操作系统中的表现形式,并会影响性能、兼容性和可用特性.

在 QEMU 中,fd0, sr0, vda, hda, sda 等设备名称有特定的含义和用途,下面是这些设备名称的基本解释:

  1. fd0:
    • 代表第一个软盘驱动器(Floppy Drive).
    • 例如,fd0 通常是软盘映像文件的设备名称.
  1. sr0:
    • 代表第一个光盘驱动器(CD-ROM Drive).
    • 例如,sr0 通常是光盘映像文件的设备名称.
  1. vdavdbvdc 等(通常是 Virtio 驱动器):
    • 这些是 Virtio 磁盘设备的命名,常用于虚拟机的磁盘设备.
    • 例如,vda 是第一个 Virtio 磁盘设备,vdb 是第二个 Virtio 磁盘设备.
  1. hdahdbhdc 等(传统 IDE 磁盘设备):
    • 这些是传统的 IDE 磁盘设备.
    • 例如,hda 是第一个 IDE 磁盘设备,hdb 是第二个 IDE 磁盘设备.
  1. sdasdbsdc 等(SCSI 磁盘设备):
    • 这些是 SCSI 磁盘设备,通常用于较新的硬件或虚拟化环境.
    • 例如,sda 是第一个 SCSI 磁盘设备,sdb 是第二个 SCSI 磁盘设备.

例子

在 QEMU 的命令行或配置文件中,你可能会看到类似以下的配置:

详细示例

下面是一个使用 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

在这个例子中:

通过这些设备名称和配置,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

在这个例子中:

-drive 参数示例

总结

使用 -drive 可以更精确地控制虚拟机的硬盘行为和性能,因此在复杂的虚拟化环境中,通常建议使用 -drive 选项.

-boot d

-hda

-cdrom

noapic 是传递给内核的一个启动参数,用于禁用 APIC(Advanced Programmable Interrupt Controller), 对于某些老旧的操作系统或不完全支持 APIC 的系统,禁用 APIC 可能提高兼容性和稳定性

-machine accel=kvm:tcg

参考

zood