模拟所需工具:1) 交叉编译工具; 2) qemu; 3) linux内核源码; 4) busybox。
IoT系统必须包含三件套:根文件系统、内核镜像、bootleader。在开发板中,一般是使用u-boot实现bootleader功能。不过由于qemu自带了bootleader,所以可以不需要u-boot。
在将linux源码和一些程序编译成对应目标架构时,需要用到交叉编译工具。如32位小端的arm,交叉编译工具的下载:
$ sudo apt install gcc-arm-linux-gnueabi
使用与gcc类似,只是相应的程序为arm-linux-gnueabi-gcc
。工具项目目录在:/usr/arm-linux-gnueabi
。
注意:交叉编译工具的版本和Linux版本不能相差太大(比如我内核是5.15的,对应gcc11是没有问题的)
下载内核源码:https://cdn.kernel.org/pub/linux/kernel/
我选择的版本是5.15.1,xz和gz都可以,不过xz压缩率比较高。
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.1.tar.xz
tar -xvf linux-5.15.1.tar.xz
# 注:下面的命令中“CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm” 可以在顶层的(也就是根目录下的)Makefile进行设置,这样下面命令中该字段可以省略
# 产生配置文件 .config
# 第一种方法:在已知开发板的情况下,采用该开发板默认的配置设置
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm vexpress_defconfig
# 第二种方法:自己进行指定的配置,menuconfig:图形配置界面
make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm menuconfig
# 一二种方法也可以结合, xxx_defconfig大体配置, menuconfig进行微调
# 这里我仅使用了第一种
# 开始编译(时间比较久)
make -j4 CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm # -j4:用多少个核心编译
# 两个将会用到的文件
linux-5.15.1/arch/arm/boot/zImage # 内核镜像
linux-5.15.1/arch/arm/boot/dts/vexpress-v2p-ca9.dtb # 设备树文件,待深入了解
xxx_defconfig
有哪些默认配置可在/arch/$ARCH/configs
获知。
为了方便后续测试,编写个shell文件:
#!/bin/bash
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel ./linux-5.15.1/arch/arm/boot/zImage \
-dtb ./linux-5.15.1/arch/arm/boot/vexpress-v2p-ca9.dtb \
-append "console=ttyAMA0" \
-nographic
$ chmod +x arm_start.sh
$ sudo ./arm_start.sh
由于没有根文件系统,所以会产生Kernel panic:
(注:退出qemu快捷键为Ctrl + A
,按完后再按X
,不要同时按! 命令:reboot -f
)
Busybox官网:http://www.busybox.net/
# 下载源码
$ wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
$ tar xvf busybox-1.35.0.tar.bz2
$ cd busybox-1.35.0/
# busybox有三种配置模式: 1)defconfig(缺省配置); 2)allyesconfig(最大配置); 3)allnoconfig(最小配置)
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
# # CONFIG_PREFIX可以设置保存处,否则默认_install/
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install CONFIG_PREFIX=./FS_space/arm32
make install
执行成功后,在./FS_space/arm32
下出现了bin linuxrc sbin usr
。
继续填充其它文件:
# 因为busybox编译时使用的是动态链接的方法,所以需要库文件
# 我们也可以通过menuconfig将其改为静态链接:
# Busybox Settings --->
# Build Options --->
# [*] Build BusyBox as a static binary (no shared libs)
$ mkdir ./FS_space/arm32/lib/
$ sudo cp -p /usr/arm-linux-gnueabi/lib/* ./FS_space/arm32/lib/
$ mkdir ./FS_space/arm32/dev/
$ sudo mknod ./FS_space/arm32/dev/tty1 c 4 1
$ sudo mknod ./FS_space/arm32/dev/tty2 c 4 2
$ sudo mknod ./FS_space/arm32/dev/tty3 c 4 3
$ sudo mknod ./FS_space/arm32/dev/tty4 c 4 4
$ sudo mknod -m 666 ./FS_space/arm32/console c 5 1
在make install时,意外地发生了报错:
在查看Makefile.custom
无关后,看这报错觉得是字符串上的问题(//多了个/)。所以直接在busybox根目录下执行:grep -r "/bin/arch"
。查找结果:busybox.links:/bin/arch
,busybox.links里面着是需要创建的文件或目录。在尝试性地将/bin/arch
改为bin/arch
后,报错点发生的转移:
然后编写了python脚本,将前面的/都去掉了,结果还报错:
后来尝试了好几个版本的busybox,版本低的在编译时会报编译器的兼容问题,高版本的1.36.0不能make defconfig
。为了避免问题的复杂化,还是回到原来版本的问题吧。
后来想到,我是在共享文件夹(win主机,因为虚拟机空间不太够,便下载到这里)中操作的。又想到有时可以使用/bin//sh
来代替/bin/sh\x00
来绕过\x00,更加感觉是这个原因,然后把项目移到虚拟机空间中重复操作,成功了。至于具体原因的细节有待研究…😶
在开发板中,根文件系统是保存在flash 中的。或者,通过nfs的方式挂载网络文件系统供linux内核启动时访问。为了模拟,我们需要虚拟一个存储介质。
$ dd if=/dev/zero of=rootfs.ext3 bs=1M count=512 # 用\x00初始化一个镜像
$ mkfs.ext3 rootfs.ext3 # 格式化
# 下面的挂载-拷贝-卸载,可以联想用U盘拷贝插入电脑拷贝数据后拔出的过程
$ sudo mount -t ext3 rootfs.ext3 /mnt/rootfs/ -o loop
$ sudo cp -rf ~/iot/linuxSRC/busybox-1.35.0/FS_space/arm32/* /mnt/rootfs
$ ls /mnt/rootfs/
bin linuxrc lost+found sbin usr
$ sudo umount /mnt/rootfs/
$ ls /mnt/rootfs/
qemu添加,尝试启动:
#!/bin/bash
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel ./linux-5.15.1/arch/arm/boot/zImage \
-dtb ./linux-5.15.1/arch/arm/boot/vexpress-v2p-ca9.dtb \
-append "root=/dev/mmcblk0 console=ttyAMA0" \
-sd ./rootfs.ext3 \
-nographic
成功执行:
不过还是有一些警告:can't run '/etc/init.d/rcS': No such file or directory
完善方案(仅供参考):
$ sudo mount -t ext3 rootfs.ext3 /mnt/rootfs/ -o loop
$ cd /mnt/rootfs/
$ ls
bin console dev lib linuxrc lost+found sbin usr
$ sudo mkdir etc mnt
$ sudo mkdir -p etc/init.d/
$ sudo vim etc/fstab
$ cat etc/fstab
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
$ sudo vim etc/init.d/rcS
$ sudo chmod 755 etc/init.d/rcS
$ sudo vim etc/inittab
$ cat etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::cttlaltdel:/bin/umount -a -r
$ sudo chmod 755 etc/inittab
$ sudo mknod dev/null c 1 3
$ cd ~ # 退出/mnt/rootfs才能umount
$ sudo umount /mnt/rootfs/
https://www.orientdisplay.com/emulating-embedded-linux-systems-with-qemu/
https://blog.csdn.net/m0_56548489/article/details/124720860
↶ 返回首页