(2023)从零开始用qemu搭建虚拟arm环境

avatar
作者
猴君
阅读量:2

用qemu搭建虚拟arm环境

引言

Qemu是什么?
Qemu是一个开源的托管虚拟机,通过纯软件来实现虚拟化模拟器,几乎可以模拟任何硬件设备。比如:Qemu可以模拟出一个ARM系统中的:CPU、内存、IO设备等,然后在这个模拟层之上,可以跑一台ARM虚拟机,这个ARM虚拟机认为自己在和硬件进行打交道,但实际上这些硬件都是Qemu模拟出来的。
在这里插入图片描述
正因为Qemu是纯软件实现的,所有的指令都要经过它的转换,所以性能非常低。所以在生产环境中,大多数的做法都是配合KVM来完成虚拟化工作,因为KVM是硬件辅助的虚拟化技术,主要负责比较繁琐的CPU和内存虚拟化,而Qemu则负责I/O虚拟化,两者合作各自发挥自身的优势,相得益彰。这部分不是重点,就不具体深入介绍了。

Qemu的两种模式
1.用户模式(User mode):利用动态代码翻译机制来执行不同主机架构的代码,例如:在x86平台上模拟执行ARM代码,也就是说:我们写一条ARM指令,传入整个模拟器中,模拟器会把整个指令翻译成x86平台的指令,然后在x86的CPU中执行。

2.系统模式(System mode):模拟整个电脑系统,利用其它VMM(Xen, KVM)来使用硬件提供的虚拟化支持,创建接近于主机性能的全功能虚拟机。
在这里插入图片描述
使用Qemu虚拟机的几种选择
利用Qemu来运行ARM虚拟机,你有2个选择:

简单方式:直接下载别人编译好的映像文件(包含了内核,根文件系统),直接执行即可。
缺点是:别人编译好的也许不适合你的需求,没法定制。

复杂方式:自己下载内核代码、根文件系统代码(例如:busybox),然后进行编译。
优点是:可以按照自己的实际需求,对内核、根文件系统机型裁剪。

在第2种复杂模式中,又可以有2个选择:
2-1. 内核代码、根文件系统代码全部自己手动编译,最后把这些编译结果手动组织在一个文件夹中,形成自己的根目录;
2-2. 利用 buildroot 整个框架,只需要手动进行配置(比如:交叉编译器在本机上的位置、输出路径、系统的裁剪),然后就可以一键编译出一个完整的系统,可以直接烧写到机器!

安装版本

VMware Workstation Pro:16.1.1
ubuntu:20.04 64位
qemu:8.1.50

搭建虚拟arm开发板:
busybox:1.36.0
kernel:5.10
u-boot:2020.10

1. VMware + ubuntu20.04 + qemu安装

参考这篇文章:【VMware + ubuntu20.04 + qemu安装】

下面将正式开始搭建虚拟的arm开发板环境

2.安装交叉编译工具

交叉编译器的作用就不需要详细解释了,因为我们是在x86平台上进行编译,而运行的平台是ARM系统,这2个平台的指令集不一样,所以需要交叉编译得到ARM系统上可以执行的程序。

sudo apt-get install gcc-arm-linux-gnueabi 

验证安装结果

dpkg -l gcc-arm-linux-gnueabi 

显示如下:
在这里插入图片描述

3.编译内核kernel

下载内核kernel压缩包

wget https://mirror.bjtu.edu.cn/kernel/linux/kernel/v5.x/linux-5.10.tar.xz 

这里我们使用 vexpress-a9 这款开发板。vexpress-a9 是 Arm 公司自己设计的一款 4 核 Cortex-A9 开发板,U-Boot、Linux Kernel 和 QEMU 对这款开发板都做了完整的支持。当然,如果想搭其它开发板,也不难,只要qemu和内核对它有成熟的支持就够了。
解压:

tar -xvf linux-5.10.tar.xz 

在解压后的linux-5.10目录下,生成vexpress开发板子的config文件:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm vexpress_defconfig 

编译32位kernel:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm 

生成的内核镱像位于arch/arm/boot/zImage
设备树 arch/arm/boot/dts/vexpress-v2p-ca9.dtb

补充:编译64位kernel

 make ARCH=arm64 defconfig CROSS_COMPILE=aarch64-linux-gnu-   # 如果需要调整配置选项,则使用menuconfig make ARCH=arm64 menuconfig CROSS_COMPILE=aarch64-linux-gnu-   make ARCH=arm64 Image -j8 CROSS_COMPILE=aarch64-linux-gnu-  

生成的内核镱像位于arch/arm/boot/Image

4.u-boot编译

拉取:

wget https://ftp.denx.de/pub/u-boot/u-boot-2020.10.tar.bz2 

下载完后,可以看到 configs 目录下有针对这款开发板的配置文件。ca9x4表示cortexA9架构,4核心,vexpress_ca9x4_defconfig

ls configs/ | grep vexpress 

在这里插入图片描述
编译:

make vexpress_ca9x4_defconfig make CROSS_COMPILE=arm-linux-gnueabihf- all 

最终编译生成 elf 格式的可执行文件 u-boot 和纯二进制文件u-boot.bin,其中 QEMU 可以启动的为 elf 格式的可执行文件 u-boot 。
在这里插入图片描述

5.制作根文件系统

内核在启动之后、执行到最后步骤时,需要挂载根文件系统,然后执行文件系统中指定的执行程序(初始化程序,本文未设置),例如:/etc/rc.local。

如果没有跟文件系统,那么内核在执行到最后就提示:panic…。

根文件系统放在哪里?
其实依赖于每个开发板支持的存储设备,可以放到Nor Flash上,也可以放到SD卡,甚至外部磁盘上。最关键的一点是你要清楚知道开发板有什么存储设备。

本文介绍了使用SD卡做为存储空间,文件格式为ext3格式。

第一步:下载、编译和安装busybox

下载busybox1.36.0:

 wget http://www.busybox.net/downloads/busybox-1.36.0.tar.bz2 

编译,安装:

make defconfig  make CROSS_COMPILE=arm-linux-gnueabi-  make install CROSS_COMPILE=arm-linux-gnueabi- 

安装完成后,会在busybox目录下生成_install目录,该目录下的程序就是单板运行所需要的命令。

第二步:形成根目录结构

先在Ubuntu主机环境下,形成目录结构,里面存放的文件和目录与单板上运行所需要的目录结构完全一样,然后再打包成镜像(在开发板看来就是SD卡),这个临时的目录结构称为根目录。

  1. 首先创建rootfs目录(根目录),根文件系统内的文件全部放到这里:
mkdir -p rootfs/{dev,etc/init.d,lib} 
  1. 把busybox中的文件复制到rootfs根目录下,主要是一些基本的命令:
sudo cp busybox-1.20.2/_install/* -r rootfs/ 
  1. 把交叉编译工具链中的库文件复制到rootfs根目录的lib文件夹下:
sudo cp -P /usr/arm-linux-gnueabi/lib/* rootfs/lib/ 
  1. 创建4个tty端终设备:
sudo mknod rootfs/dev/tty1 c 4 1  sudo mknod rootfs/dev/tty2 c 4 2  sudo mknod rootfs/dev/tty3 c 4 3  sudo mknod rootfs/dev/tty4 c 4 4 

第三步:制作根文件系统镜像

制作根文件系统镜像 根文件系统镜像就相当于一个硬盘,就是把上面rootfs根目录中的所有文件复制到这个硬盘中。

第一种:
生成512M大小的镜像:

dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=32 

格式化成ext3文件系统:

mkfs.ext3 a9rootfs.ext3 

挂载,将文件拷贝到镜像中:

sudo mkdir tmpfs  sudo mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop  sudo cp -r rootfs/*  tmpfs/  sudo umount tmpfs 

qemu启动ARM虚拟机运行:

qemu-system-arm -M vexpress-a9 -m 512M -kernel /path/to/kernel/dir/arch/arm/boot/zImage -dtb  /path/to/kernel/dir/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0  console=ttyAMA0" -sd a9rootfs.ext3 

“从内核启动打印,到命令行提示符出现”
在这里插入图片描述

第二种:
生成512M大小的磁盘镜像:

qemu-img create -f raw disk.img 512M 

把磁盘镜像格式化成ext4文件系统:

mkfs -t ext4 ./disk.img 

将rootfs根目录中的所有文件复制到磁盘镜像中 操作步骤是:创建挂载点-挂载-复制文件-卸载:

mkdir tmpfs  sudo mount -o loop ./disk.img tmpfs/   sudo cp -r rootfs/* tmpfs/ sudo umount tmpfs 

使用file指令检查一下:

file disk.img 

在这里插入图片描述
qemu启动ARM虚拟机运行:

qemu-system-arm -M vexpress-a9 -m 512M -kernel /path/to/kernel/dir/arch/arm/boot/zImage -dtb  /path/to/kernel/dir/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd disk.img 

“从内核启动打印,到命令行提示符出现”

测试HelloWorld应用程序

在Ubuntu任意一个目录,编写HelloWorld可执行程序hello.c:

touch hello.c vi hello.c 

按i进入输入模式,复制下面代码,然后esc,shift+:,wq保存退出:

#include <stdio.h>  int main()  {          printf("HelloWorld! \n");     return 0;  } 

交叉编译hello.c,得到arm的可执行程序hello:

arm-linux-gnueabi-gcc hello.c -o hello 

通过file指令,查看一下hello程序:

file hello 

在这里插入图片描述
把hello可执行程序复制到磁盘镜像disk.img中 操作步骤是:挂载-复制文件-卸载:

sudo mount -o loop ./disk.img tmpfs/   cp hello tmpfs/  sudo umount tmpfs 

执行hello程序 再次启动虚拟机,此时可以在根目录下面看到hello文件,直接执行即可看到输出结果。
在这里插入图片描述

总结:
在以上的操作步骤中,我们把一个ARM系统在启动应用程序之前,所需要的程序都手动编译、操作了一遍。看一遍很容易就明白,亲手操作一遍印象会更深刻。

这里的操作过程有些还需要继续深入,比如:在系统启动之后,自动挂载宿主机(Ubuntu系统)中的某个文件夹,这样就可以把hello等可执行程序复制到挂载目录中,然后在ARM系统中直接执行了,而不用再执行下面在一连串的操作(停止虚拟机-挂载磁盘镜像-复制文件-卸载-启动虚拟机)。

如何关闭qemu虚拟机

停止虚拟机 在Ubuntu另一个终端窗口中,通过killall指令来停止。

killall qemu-system-arm 

或者直接 Ctrl+a 再按x,直接退出qemu环境。

补充:

关于”make: arm-linux-gnueabihf-gcc: Command not found“问题

解决方法:sudo apt-get install gcc-arm*
参考:https://blog.csdn.net/HGGshiwo/article/details/120479087

关于qemu启动ARM虚拟机运行指令解析

qemu-system-arm -M vexpress-a9 -m 512M -kernel /path/to/kernel/dir/arch/arm/boot/zImage -dtb  /path/to/kernel/dir/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0  console=ttyAMA0" -sd a9rootfs.ext3 

-M vexpress-a9 模拟vexpress-a9单板,你可以使用-M ?参数来获取该qemu版本支持的所有单板

-m 512M 单板运行物理内存512M

-kernel /path/to/kernel/dir/arch/arm/boot/zImage 告诉qemu单板运行内核镜像路径

-nographic 不使用图形化界面,只使用串口

-append “console=ttyAMA0” 内核启动参数,这里告诉内核vexpress单板运行,串口设备是那个tty。

因为不同单板串口驱动类型不尽相同,创建的tty设备名当然也是不相同的。那vexpress单板的tty设备名是哪个呢? 其实这个值可以从生成的.config文件CONFIG_CONSOLE宏找到。
如果搭建其它单板,需要注意内核启动参数的console=参数值,同样地,可从生成的.config文件中找到。

参考文献:

一步步教你:如何用Qemu来模拟ARM系统

从零使用qemu模拟器搭建arm运行环境

【十】搭建基于qemu的仿真环境与应用

如果想了解更多细节,包括:创建设备结点,设置初始化进程/etc/rcS,u-boot加载内核等,可参考:
【宅学部落的qemu教程】

或者其他优秀教程
Linux利器:QEMU!用它模拟开发板能替代真开发板?
qemu-system-arm功能选项整理

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!