用QEMU搭建Ubuntu虚拟环境
用QEMU搭建Ubuntu虚拟环境
之前有介绍《基于QEMU的内核调试环境搭建》:
https://notes.z-dd.online/2024/03/06/%E5%9F%BA%E4%BA%8EQEMU%E7%9A%84%E5%86%85%E6%A0%B8%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
这里主要想搭建一个基于QEMU的完整ubuntu2404的调试环境,方便调试最新内核。
准备内核
这里没啥太多好说的,直接 make 就是行,编译配置使用默认和kvm的配置就好,如果有其他需求,可在此基础上继续修改。
#使用默认编译配置
make defconfig
#在默认配置基础上设置编译选项,使编译后的镜像可以作为 kvm 上的虚拟机运行
make kvm_guest.config
准备文件系统
之前只制作了简单的文件系统(基于busybox、或buildroot等),这里想做个发行版(Ubuntu-2404)的根文件系统,而且是完整的那种。
整个操作主要参考自 syzkaller 的 create-image.sh create-image.sh 脚本,但是精简了许多内容。
构建完整文件系统
debootstrap 是这里主要用到的一个工具 。这个命令可以用于在文件系统中构建一个完整的 debian 文件系统(其他发行版,如ubuntu 也可以)。这个工具只是一个脚本,依赖极小。
debootstrap 是一个可以快速获得基本 Debian 系统的一个工具, 你可以将 Debootstrap 看作是一种特殊的安装工具. 它不同于 Debian Installter , 不需要安装用的CD/DVD ISO, 仅需连接到 Debian 软件仓库, 软件仓库简介(英文) . 无论你是否使用 Debian , 只要是任何的 Linux/GNU 发行版, 例如 Fedora/Gentoo/Arch/OpenSUSE, 甚至是 Ubuntu , 均可运行 debootstrap
来自官方: https://wiki.debian.org/zh_CN/Debootstrap
安装debootstrap:
sudo apt-get install debootstrap
之后,我们使用该命令构建一个 ubuntu 的完整文件系统。
#指定镜像源,留空表示使用默认源
MIRROR=https://mirrors.aliyun.com/ubuntu/
#发行版版本代号
RELEASE=noble
#完整文件系统存放目录
ROOT_DIR=ubuntu_root
#构建,使用 --include 可自行添加组件,这里主要是systemd,openssh-server
sudo debootstrap --include systemd,openssh-server $RELEASE $ROOT_DIR $MIRROR
注:这里的
$RELEASE是发行版版本的代号,要与/usr/share/debootstrap/scripts/目录下的相匹配!
相关配置修改
修改用户及密码
安装完成后,我们修改 root 用户的密码。因为只是开发环境,所以这里直接不使用密码。(即删除 /etc/passwd 中密码项的 x)
set -u
sudo sed -i '/^root/ { s/:x:/::/ }' $ROOT_DIR/etc/passwd
小心!运行命令前确保 ROOT_DIR 已设置。此处通过 set -u 进行了检查。
set -u 是一个 Shell 脚本选项,用于在脚本中启用严格模式。如果脚本中尝试使用未定义的变量,Shell 会立即抛出错误并终止执行。这有助于避免因拼写错误或未初始化变量而导致的潜在问题。
网络配置
之后我们进行网络的配置。运行下面命令,创建 /etc/netplan/01-netcfg.yaml 文件。配置中我们让 eth0 设备使用 DHCP 协议自动获取 IP 地址。
cat << EOF | sudo tee -a $ROOT_DIR/etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: yes
nameservers:
addresses:
- 8.8.8.8
EOF
ubuntu 使用 netplan 进行网络配置,debian 的配置方法则不同,可以参考 syzkaller 的脚本。
修改挂载
然后我们修改 /etc/fstab 文件。将 /dev/root 挂载为根文件系统。此处应当是为了控制后续挂载的行为,如果不增加此项,后面将启动失败。
echo '/dev/root / ext4 defaults 0 0' | sudo tee -a $ROOT_DIR/etc/fstab
配置ssh(可选)
这里可以创建一个 ssh key,方便后面通过 ssh 连接。
ssh-keygen -f $RELEASE.id_rsa -t rsa -N ''
sudo mkdir -p $ROOT_DIR/root/.ssh
cat $RELEASE.id_rsa.pub | sudo tee -a $ROOT_DIR/root/.ssh/authorized_keys
其他自己想要的修改
创建根文件系统镜像
最后,我们按照之前的方式创建根文件系统镜像。
#镜像挂载目录
MOUNT_DIR=${RELEASE}_rootfs
#新建4G的Img文件
truncate -s 4G $RELEASE.img
#格式化镜像文件为ext4
mkfs.ext4 -F $RELEASE.img
mkdir $MOUNT_DIR
#挂载镜像文件
sudo mount -o loop $RELEASE.img $MOUNT_DIR
#复制根文件系统到挂载的镜像文件
sudo cp -a $ROOT_DIR/. $MOUNT_DIR/
#卸载
sudo umount $MOUNT_DIR
#移除挂载目录
rmdir $MOUNT_DIR
运行虚拟机
最后,使用 QEMU 运行我们的内核和根文件系统。
qemu-system-x86_64 \
-enable-kvm \
-m 8G \
-smp 4 \
-kernel /path/to/your/bzImage \
-append "console=ttyS0 root=/dev/vda net.ifnames=0" \
-drive file=/path/to/your.img,format=raw,if=virtio \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:10022-:22 \
-nographic
其中:
-enable-kvm启用 KVM 加速;-m设定 8G 的内存;-smp设置 4 核 CPU;-kernel指定内核镜像;-append设置内核选项,其中console=ttyS0设定使用串口通信,root=/dev/vda设定使用 /dev/vda 设备作为根文件系统,net.ifnames=0设定禁用网卡重命名,以便我们之前设置的 eth0 配置可以正常生效。-drive设定根文件系统镜像,其中if=virtio指定接口类型为 virtio(虚拟化接口),对应的设备为 /dev/vda。-device设定一个网卡设备,类型为virtio-net-pci(虚拟网卡),名为 eth0(和配置对应)。-netdev user,id=net0设置 net0 上宿主机与虚拟机间的网络使用 “用户网络”(User Networking),此时虚拟机的 IP 地址由 QEMU 的 DHCP 服务提供。hostfwd=tcp:127.0.0.1:10022-:22将虚拟机上的 22(ssh)端口转发到宿主机上的 10022 端口。这时宿主机通过访问自身的 10022 端口即可 ssh 登陆虚拟机。-nographic禁用图像,使用串口通信。
运行上述命令,启动虚拟机。一小段时间后出现登陆提示,输入 root 用户名后,直接登入系统:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 7.0.0-rc5-00226-g46b513250491 x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
root@debian:~#
查看内核版本(7.0.0-rc5),和网络设备信息。
没问题的话,此时虚拟机应当能够访问互联网。可以通过 apt 安装所需包,使用之前生成的 ssh 私钥登陆虚拟机:
ssh -i /your/path/to.id_rsa -p 10022 root@localhost
至此,整个环境基本上算完成了。






