1. 环境准备与依赖安装
在Ubuntu 22.04上搭建RISC-V QEMU虚拟化环境,首先需要安装必要的依赖包。这些工具链和库文件是后续编译和调试的基础,缺一不可。我建议直接使用apt包管理器安装,避免从源码编译的繁琐过程。
打开终端,执行以下命令安装基础开发工具和交叉编译工具链:
sudo apt update
sudo apt install -y autoconf automake autotools-dev curl python3 python3-pip \
libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo \
gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build git cmake \
libglib2.0-dev libslirp-dev gcc-riscv64-linux-gnu
这里特别要注意gcc-riscv64-linux-gnu这个包,它是Ubuntu官方仓库提供的RISC-V交叉编译器。实测下来这个版本的稳定性相当不错,避免了自行编译工具链可能遇到的各种兼容性问题。
安装完成后,验证交叉编译器是否正常工作:
riscv64-linux-gnu-gcc --version
如果输出类似"riscv64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"的信息,说明工具链安装成功。
接下来还需要安装一些QEMU相关的依赖库。虽然Ubuntu仓库提供了预编译的QEMU包,但为了获得最新的RISC-V虚拟化支持,我们仍然需要从源码编译特定版本的QEMU:
sudo apt install -y libfdt-dev libpixman-1-dev libcap-ng-dev libattr1-dev \
libusb-1.0-0-dev libbrlapi-dev libncursesw5-dev libspice-server-dev \
libusbredirparser-dev libiscsi-dev librados-dev libseccomp-dev \
libsnappy-dev liblzo2-dev libssh-dev libvde-dev libvdeplug-dev \
libxml2-dev libnfs-dev libaio-dev libjemalloc-dev
这些依赖包确保了QEMU能够支持各种虚拟化功能和硬件模拟。我在实际搭建过程中发现,如果缺少某些开发库,后续的编译过程可能会静默失败,或者运行时出现难以调试的段错误。
2. 编译支持RISC-V的QEMU
虽然Ubuntu 22.04官方仓库提供了QEMU软件包,但版本相对较旧,可能缺少最新的RISC-V虚拟化特性。因此我们需要从源码编译QEMU 10.0.0版本,这个版本对RISC-V KVM支持比较完善。
首先下载QEMU源码并切换到稳定版本:
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
git checkout v10.0.0
QEMU使用了子模块来管理一些依赖组件,需要初始化和更新:
git submodule init
git submodule update --recursive
这个过程可能会花费一些时间,因为需要下载多个子模块的代码。如果网络连接不稳定,可以考虑使用--depth 1参数来减少下载量。
配置QEMU编译选项时,我们只针对RISC-V架构进行编译,这样可以显著减少编译时间:
./configure --target-list="riscv32-softmmu riscv64-softmmu" \
--enable-kvm --enable-virtfs --enable-slirp \
--enable-debug --enable-debug-info
这里有几个关键选项值得说明:--enable-kvm开启KVM虚拟化支持,这是实现硬件加速的关键;--enable-debug和--enable-debug-info包含调试信息,为后续的内核调试做准备。
开始编译QEMU:
make -j$(nproc)
使用-j$(nproc)参数可以让编译过程充分利用所有CPU核心,大大加快编译速度。在我的16核机器上,完整编译大约需要15-20分钟。
编译完成后,验证QEMU是否支持RISC-V:
./build/qemu-system-riscv64 --version
应该能看到输出类似"QEMU emulator version 10.0.0"的信息。为了使用方便,建议将编译好的QEMU添加到系统路径中:
echo 'export PATH=$PATH:'$(pwd)/build >> ~/.bashrc
source ~/.bashrc
3. 获取和编译Linux内核
有了QEMU模拟器,接下来需要准备RISC-V架构的Linux内核。我们使用官方内核源码并进行交叉编译。
克隆Linux内核源码仓库:
git clone https://github.com/torvalds/linux.git linux-riscv
cd linux-riscv
建议切换到稳定的LTS版本分支,避免开发版可能的不稳定性:
git checkout v6.6
配置交叉编译环境变量:
export ARCH=riscv
export CROSS_COMPILE=riscv64-linux-gnu-
创建专门的编译输出目录,保持源码树的干净:
mkdir ../build-riscv64
make O=../build-riscv64 defconfig
使用defconfig会生成一个默认的配置,但为了调试需要,我们还需要调整一些配置选项:
make O=../build-riscv64 menuconfig
在配置界面中,需要确保以下选项被启用:
CONFIG_DEBUG_INFO- 包含调试信息CONFIG_GDB_SCRIPTS- GDB调试脚本支持CONFIG_KVM- KVM虚拟化支持CONFIG_SERIAL_EARLYCON- 早期控制台输出CONFIG_FRAME_POINTER- 帧指针调试
保存配置后开始编译内核:
make O=../build-riscv64 -j$(nproc)
内核编译过程相对较长,在我的机器上大约需要30分钟。编译完成后,在build-riscv64/arch/riscv/boot/目录下会生成Image文件,这就是我们要使用的内核镜像。
验证内核镜像是否生成:
file ../build-riscv64/arch/riscv/boot/Image
应该显示为"Linux kernel ARM64 boot executable Image"之类的信息。
4. 构建最小根文件系统
Linux内核需要一个根文件系统才能正常启动。我们使用BusyBox来构建一个极简的根文件系统,包含最基本的命令行工具。
首先下载和编译BusyBox:
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
配置BusyBox为静态链接,避免依赖外部库:
make defconfig
make menuconfig
在配置界面中,选择以下选项:
Settings -> Build static binary (no shared libs)- 静态链接Settings -> Cross compiler prefix- 设置为riscv64-linux-gnu-
开始编译BusyBox:
make -j$(nproc) install
编译完成后,创建根文件系统目录结构:
mkdir -p _install/{etc/init.d,dev,proc,sys,apps}
创建基本的设备节点:
sudo mknod _install/dev/console c 5 1
sudo mknod _install/dev/null c 1 3
设置初始化脚本_install/etc/init.d/rcS:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "Welcome to RISC-V Linux!"
exec /bin/sh
给初始化脚本添加执行权限:
chmod +x _install/etc/init.d/rcS
使用cpio打包根文件系统:
cd _install
find . | cpio -o -H newc > ../../rootfs.cpio
cd ../..
这样就生名为rootfs.cpio的根文件系统镜像,后续启动QEMU时会用到这个文件。
5. 配置KVMTOOL和libfdt
为了获得更好的虚拟化性能,我们需要编译KVMTOOL,这是一个轻量级的KVM管理工具。但首先需要解决libfdt库的依赖问题。
RISC-V交叉工具链默认不包含libfdt库,需要手动编译安装:
git clone https://github.com/dgibson/dtc.git
cd dtc
export CC="riscv64-linux-gnu-gcc -mabi=lp64d -march=rv64gc"
make libfdt -j$(nproc)
获取系统根目录路径:
SYSROOT=$(riscv64-linux-gnu-gcc -print-sysroot)
安装libfdt到工具链的系统目录:
sudo make NO_PYTHON=1 NO_YAML=1 DESTDIR=$SYSROOT PREFIX=/usr \
LIBDIR=/usr/lib64/lp64d install-lib install-includes
现在编译KVMTOOL:
git clone https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git
cd kvmtool
export ARCH=riscv
export CROSS_COMPILE=riscv64-linux-gnu-
make lkvm-static
使用strip工具减小二进制文件大小:
riscv64-linux-gnu-strip lkvm-static
将编译好的KVMTOOL复制到根文件系统中备用:
cp lkvm-static ../busybox-1.36.1/_install/apps/
6. 启动QEMU虚拟机
现在所有组件都已准备就绪,可以启动RISC-V虚拟机了。使用以下命令启动QEMU:
qemu-system-riscv64 -cpu rv64 -M virt -m 512M -nographic \
-kernel build-riscv64/arch/riscv/boot/Image \
-initrd rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 earlycon=sbi"
各个参数的含义如下:
-cpu rv64:指定CPU类型为RV64GC-M virt:使用QEMU的virt机器类型-m 512M:分配512MB内存-nographic:不使用图形界面,直接输出到控制台-kernel:指定内核镜像路径-initrd:指定初始根文件系统-append:内核启动参数
如果一切正常,应该能看到内核启动日志,最后出现BusyBox的shell提示符。可以执行一些基本命令如ls、cat /proc/cpuinfo来验证系统是否正常工作。
为了测试KVM功能,可以在虚拟机内加载KVM内核模块:
insmod /apps/kvm.ko
然后使用KVMTOOL启动嵌套虚拟化:
/apps/lkvm-static run -m 128 -c2 --console serial -p "console=ttyS0 earlycon" -k /apps/Image --debug
这个过程可能会遇到一些权限问题,需要确保/dev/kvm设备存在且权限正确。
7. GDB内核调试配置
内核调试是开发过程中不可或缺的部分。首先在编译内核时确保开启了调试选项,然后使用QEMU的调试功能。
启动QEM时添加调试参数:
qemu-system-riscv64 -cpu rv64 -M virt -m 512M -nographic \
-kernel build-riscv64/arch/riscv/boot/Image \
-initrd rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 earlycon=sbi" \
-s -S
-s参数表示在1234端口开启GDB调试服务器,-S参数表示启动时暂停CPU等待调试器连接。
在另一个终端中启动GDB:
riscv64-linux-gnu-gdb build-riscv64/vmlinux
在GDB中连接QEMU:
target remote :1234
现在可以设置断点、单步执行、查看变量等。常用的GDB命令包括:
b function_name:在函数处设置断点c:继续执行ni:单步执行(不进入函数)si:单步执行(进入函数)info registers:查看寄存器值x/10i $pc:反汇编当前指令
为了更方便地调试,可以加载内核提供的GDB脚本:
source build-riscv64/vmlinux-gdb.py
这个脚本提供了很多内核调试专用的命令,如lx-symbols自动加载符号表,lx-dmesg查看内核日志等。
8. 常见问题与解决方案
在环境搭建过程中,我遇到过不少坑,这里分享一些常见问题的解决方法。
问题1:QEMU启动失败,提示缺少KVM支持
检查CPU是否支持虚拟化:
cat /proc/cpuinfo | grep vmx # Intel
cat /proc/cpuinfo | grep svm # AMD
确保KVM内核模块已加载:
sudo modprobe kvm
sudo modprobe kvm_intel # 或 kvm_amd
检查当前用户是否在kvm组中:
sudo usermod -aG kvm $USER
问题2:内核编译错误,提示缺少依赖
确保所有必要的开发包都已安装:
sudo apt install -y libssl-dev libelf-dev bc
问题3:GDB连接失败或无法识别目标
检查GDB版本是否支持RISC-V架构:
riscv64-linux-gnu-gdb --version
确保编译内核时开启了CONFIG_DEBUG_INFO和CONFIG_GDB_SCRIPTS选项。
问题4:根文件系统启动失败
检查init脚本的权限和执行路径,确保BusyBox编译为静态链接:
file busybox-1.36.1/busybox
应该显示"statically linked"而不是"dynamically linked"。
问题5:性能问题
如果虚拟机运行缓慢,可以尝试启用KVM加速:
qemu-system-riscv64 -enable-kvm -cpu host ...
增加内存和CPU核心数:
qemu-system-riscv64 -m 2G -smp 4 ...
9. 高级调试技巧
掌握了基础调试方法后,可以进一步使用一些高级调试技巧来提高效率。
内核oops分析:当内核发生panic时,会打印oops信息。可以使用gdb解析oops中的地址:
gdb build-riscv64/vmlinux
l *0xffffffffc0000000
动态输出:除了重新编译内核外,可以使用动态调试功能:
echo 'file kernel/* +p' > /sys/kernel/debug/dynamic_debug/control
性能分析:使用perf工具进行性能分析:
perf record -g -e cycles:u -- ./workload
perf report
内存调试:开启内核的内存调试选项,检测内存泄漏和使用后释放等问题:
echo 1 > /proc/sys/kernel/kmemleak
kmemleak_scan
跟踪点:使用内核跟踪点来监控特定事件:
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
cat /sys/kernel/debug/tracing/trace_pipe
这些高级技巧需要结合具体的问题场景来使用,在实际调试过程中往往能起到关键作用。
环境搭建和调试是一个反复迭代的过程,遇到问题时不要气馁,多查阅文档和社区资源。RISC-V生态还在快速发展中,每个版本都可能带来新的特性和改进。保持环境更新,定期同步最新的内核和工具链代码,能够获得更好的体验和性能。
327



被折叠的 条评论
为什么被折叠?



