嵌入式软件开发依赖于嵌入式硬件设备,比如:开发板、外部模块设备等,但是如果只是想学习、研究Linux内核,想学习Linux内核的架构,工作模式,需要修改一些代码,重新编译并烧写到开发板中进行验证,这样未必有些复杂,并且为此专门购买各种开发版,浪费资金,开会演示效果还需要携带一大串的板子和电线,不胜其烦。然而Qemu的使用可以避免频繁在开发板上烧写版本,如果进行的调试工作与外设无关,仅仅是内核方面的调试,Qemu模拟ARM开发环境完全可以完美地胜任。
下面简单介绍下我的Qemu开发环境搭建过程
1. 环境
由于在开发过程中也需要Windows系统下的一些工具,双系统环境切换操作系统时必须重启,于是放弃了以前搭建的双系统环境,而采用在PC的Windows10系统下通过VirtualBox虚拟机安装Xubuntu系统进行开发,避免了双系统开发中需要不断重启切换PC系统的问题。Xubuntu系统和Ubuntu系统大同小异,只是桌面封装更加简洁。
1.1 所使用环境
Ubuntu-18.04.1
或:
PC系统:Windows10
虚拟机:VirtualBox-5.18
虚拟机系统:Xubuntu
模拟的开发板:vexpress
1.2 搭建环境时使用的工具
qemu-4.2.0
linux-4.14.172(Linux Kernel)
u-boot-2017.05
busybox-1.31.1
arm-linux-gnueabi-gcc (Linaro 7.5.0-3)
为了将Qemu搭建开发环境资料统一放到一起,创建~/qemu目录,所有相关文件全部放置在一起;
2. 安装交叉编译工具
# sudo apt install gcc-arm-linux-gnueabi
复制
查看安装是否成功:
$ arm-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabi/7/lto-wrapper
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --with-system-zlib --with-target-system-zlib --enable-multiarch --enable-multilib --disable-sjlj-exceptions --with-arch=armv5t --with-float=soft --disable-werror --enable-multilib --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabi --program-prefix=arm-linux-gnueabi- --includedir=/usr/arm-linux-gnueabi/include
Thread model: posix
gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)
复制
3. 安装Qemu工具
有两种方法可以在Linux环境下安装Qemu工具,第一种直接使用XUbuntu系统的apt工具安装,但是这种方法安装的Qemu系统版本不是最新的,如果需要安装最新版本的Qemu工具,就需要第二种方法,通过Git工具下载源码,切换到最新分支再去编译安装了;但是一般情况下通过git下载代码速度极慢,我们可以使用第三种方法,找到要下载的qemu版本,使用迅雷下载;具体操作如下所述:
3.1 快速安装Qemu
# sudo apt install qemu
复制
这种情况下安装的qemu版本可能不是最新版本;如果想要安装最新版本的qemu,还得使用下边介绍的使用源代码编译安装的方法;
3.2 下载Qemu源码编译安装
3.2.1 下载Qemu源码
- 从Git服务器下载Qemu代码,记着在下载之前选择并切换需要的源码分支:
# git clone git://git.qemu-project.org/qemu.git
# cd qemu
# git checkout -b stable-*** remotes/origin/stable-***
复制
-
在windows系统下使用迅雷下载
登陆download.qemu.org网站,选择需要的版本,点击下载,或者右键后选使用迅雷下载,速度会更快:
在这里选择qemu-4.2.0.tar.xz使用;
3.2.2 安装
在配置qemu之前,需要安装一些依赖的库或者软件包:
# sudo apt-get install zlib1g-dev
# sudo apt-get install libglib2.0-0
# sudo apt-get install libglib2.0-dev
# sudo apt-get install libtool
# sudo apt-get install libsdl1.2-dev
# sudo apt-get install autoconf
复制
解压源代码:
# tar -xvf qemu-4.2.0.tar.xz
复制
为了防止编译后文件比较乱,选择创建build目录作为编译中间目标路径:
# cd qemu-4.2.0/
# mkdir build
# cd build/
复制
配置、编译并安装Qemu:
# ../configure --target-list=arm-softmmu --audio-drv-list=
# make
# make install
复制
3.2.3 在编译过程中可能出现的问题
# ../configure --target-list=arm-softmmu --audio-drv-list=
ERROR: pkg-config binary 'pkg-config' not found
复制
缺少库文件,按照上一步中的步骤安装库文件;
# ../configure --target-list=arm-softmmu --audio-drv-list=
ERROR: pixman >= 0.21.8 not present.
Please install the pixman devel package.
复制
可以通过apt-cache查询缺少的依赖库:
# apt-cache search pixman
libpixman-1-0 - pixel-manipulation library for X and cairo
libpixman-1-dev - pixel-manipulation library for X and cairo (development files)
复制
安装缺少的依赖库:
# sudo apt-get install libpixman-1-0
# sudo apt-get install libpixman-1-dev
复制
3.3 查看Qemu版本
# qemu-system-arm --version
QEMU emulator version 4.2.0
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
复制
3.4 查看Qemu支持的开发板
Qemu工具支持大量开发板的虚拟,现存的大部分常用开发板都能很好地支持。通过下面的命令操作可以看到当前版本的Qemu工具支持的开发板列表:
# qemu-system-arm -M help
......
vexpress-a15 ARM Versatile Express for Cortex-A15
vexpress-a9 ARM Versatile Express for Cortex-A9
......
复制
3.5 运行Qemu
该操作目前还不能运行,因为还没有编译内核,如果手边有编译好的别的版本的zImage文件,可以通过下面命令尝试运行看下效果。
# qemu-system-arm -M vexpress-a9 -m 512M -kernel ./zImage -dtb ./vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
-M 指定开发板
-m 指定内存大小
-kernel 指定内核文件
-dtb 指定dtb文件
-nographic 指定不需要图形界面
-append 指定扩展显示界面,串口或者LCD
复制
实例参考:
# qemu-system-arm -M vexpress-a9 -m 512M -kernel ~/qemu/zImage -dtb ~/qemu/vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
-M vexpress-a9 模拟vexpress-a9板,可以使用-M ?参数来查询qemu支持的所有单板
-m 512M 单板物理内存512M
-kernel ~/qemu/zImage 指定内和镜像及路径
-dtb ~/qemu/vexpress-v2p-ca9.dtb 指定单板的设备树文件
-nographic 不使用图形界面,使用串口
-append "console=ttyAMA0" 指定内核启动参数,串口设备使用ttyAMA0
复制
4. 配置并编译Linux内核
4.1 下载Linux内核
通过众所周知的内核下载网站www.kernel.org下载需要的内核版本,这里我下载的是相对来说最新的长期支持的内核版本linux-4.4.157。
4.2 解压Linux内核
# tar -xvf linux-4.4.157.tar.xz
复制
4.3 编译Linux内核
配置
$ make vexpress_defconfig ARCH=arm O=./object
make[1]: Entering directory '/home/xiami/tool/linux-4.14.172/object'
HOSTCC scripts/basic/fixdep
GEN ./Makefile
HOSTCC scripts/kconfig/conf.o
SHIPPED scripts/kconfig/zconf.tab.c
SHIPPED scripts/kconfig/zconf.lex.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
make[1]: Leaving directory '/home/xiami/tool/linux-4.14.172/object'
复制
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig -j4 O=./object
复制
全编译
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 O=./object
复制
或者在Makefile中配置默认值,指定ARCH和CROSS_COMPILE,免得每次编译都需要带参数;
# make vexpress_defconfig
# make zImage -j4
# make modules -j4 // 编译驱动模块
# make dtbs // 编译设备树
复制
得到编译文件:
arch/arm/boot/zImage
arch/arm/boot/dts/vexpress-v2p-ca9.dtb
复制
分别将编译生成的zImage和vexpress-v2p-ca9.dtb文件放到~/qemu目录;
# cp arch/arm/boot/zImage ~/qemu
# cp arch/arm/boot/dts/vexpress-v2p-ca9.dtb ~/qemu
复制
4.4 Qemu启动命令
# qemu-system-arm -M vexpress-a9 -m 512M -kernel kernel/linux-4.4.157/arch/arm/boot/zImage -dtb kernel/linux-4.4.157/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
复制
Qemu的启动命令需要带好几个参数,完成启动命令比较长,每次都输入很可能会出现错误,为了使用方便,可以将该命令放到shell脚本中执行:
# cat boot.sh
#! /bin/sh
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel kernel/linux-4.4.157/arch/arm/boot/zImage \
-dtb kernel/linux-4.4.157/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-nographic \
-append "console=ttyAMA0"
复制
启动日志
内核成功启动,内核的启动打印信息非常多。启动最后出错是因为没有挂载根文件系统。
$ qemu-system-arm -M vexpress-a9 -m 5
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
复制
3) 将rootfs烧写到SD卡:
# sudo mount -t ext3 rootfs.ext3 /mnt -o loop
# sudo cp -rf rootfs/* /mnt/
# sudo umount /mnt
复制
在开发过程中,如果需要修改SD卡中的内容,可以将SD卡的镜像rootfs.ext3挂载到/mnt目录下,直接操作/mnt来修改;
# sudo mount -t ext3 rootfs.ext3 /mnt -o loop
复制
# cp rootfs.ext3 ~/qemu
复制
6. 验证
1) Qemu启动命令:
# qemu-system-arm -M vexpress-a9 -m 512M -kernel ~/qemu/zImage -dtb ~/qemu/vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd rootfs.ext3
复制
2) 启动脚本:
# boot.sh
#! /bin/sh
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel ~/qemu/zImage \
-dtb ~/qemu/vexpress-v2p-ca9.dtb \
-nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd rootfs.ext3
复制
以上为在串口终端启动系统,按照以下的启动命令可以使用LCD屏作为输出启动系统。
3) 图形化启动内核:
qemu-system-arm -M vexpress-a9 -m 512M -kernel ~/qemu/zImage -dtb ~/qemu/vexpress-v2p-ca9.dtb -append "root=/dev/mmcblk0 rw console=tty0" -sd rootfs.ext3
复制
4) 启动验证:
request_module: kmod_concurrent_max (0) close to 0 (max_modprobes: 50), for module binfmt-464c, throttling...
request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
Starting init: /sbin/init exists but couldn't execute it (error -8)
request_module: kmod_concurrent_max (0) close to 0 (max_modprobes: 50), for module binfmt-464c, throttling...
request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now
Starting init: /bin/sh exists but couldn't execute it (error -8)
Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.172 #1
Hardware name: ARM-Versatile Express
[<8010f180>] (unwind_backtrace) from [<8010b444>] (show_stack+0x10/0x14)
[<8010b444>] (show_stack) from [<806616cc>] (dump_stack+0x94/0xa8)
[<806616cc>] (dump_stack) from [<8011d67c>] (panic+0xdc/0x248)
[<8011d67c>] (panic) from [<80674634>] (kernel_init+0x104/0x114)
[<80674634>] (kernel_init) from [<801076a8>] (ret_from_fork+0x14/0x2c)
---[ end Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
QEMU: Terminated
复制
如上问题是由于编译生成的busybox工具,是x86环境下使用的:
# file bin/busybox
bin/busybox: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=7fe433943e201f5337be6116a883d54fc1a4a349, stripped
复制
是因为在安装busybox的时候,使用了make install,应该使用
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install
复制
编译工具生成arm平台使用的busybox工具:
# file rootfs/bin/busybox
rootfs/bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=cbcd33b8d6c946cb19408a5e8e714de554c87f52, stripped
复制
再次验证:
rtc-pl031 10017000.rtc: setting system clock to 2018-09-24 13:22:14 UTC (1537795334)
ALSA device list:
#0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 33
input: ImExPS/2 Generic Explorer Mouse as /devices/platform/smb/smb:motherboard/smb:motherboard:iofpga@7,00000000/10007000.kmi/serio1/input/input2
EXT4-fs (mmcblk0): mounting ext3 file system using the ext4 subsystem
EXT4-fs (mmcblk0): recovery complete
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
VFS: Mounted root (ext3 filesystem) on device 179:0.
Freeing unused kernel memory: 284K
random: nonblocking pool is initialized
can't run '/etc/init.d/rcS': No such file or directory
Please press Enter to activate this console.
/ #
/ #
/ # uname -a
Linux (none) 4.4.157 #1 SMP Sun Sep 23 21:11:22 CST 2018 armv7l GNU/Linux
复制
至此,Qemu启动Linux内核并挂载跟文件系统已经启动成功,通过串口终端可以正常和系统进行简单功能的交互。
打印中提示的不能运行/etc/init.d/rcS问题,只需要添加/etc/init.d/rcS文件即可,文件内容可以是提示语句。
# cat /etc/init.d/rcS
Hello Qemu Linux!
复制
注意,要在创建/etc/init.d/rcS文件时,记着修改该文件的可执行权限,否则启动过程中会报错:
Freeing unused kernel memory: 1024K
random: crng init done
can't run '/etc/init.d/rcS': Permission denied
Please press Enter to activate this console.
复制
7. 退出Qemu环境
Qemu环境搭建好之后,在出错时需要关闭并重新启动Qemu,不用的时候需要关闭Qemu。
1)手动退出Qemu
Ctrl + A; X
复制
操作之后,终端上会打印:
QEMU: Terminated
复制
2)强制退出Qemu
有时候会发现无法通过shutdown等工具关闭,因为Qemu也是一个进程,可以通过杀掉Qemu进程的方法关闭Qemu模拟环境。
# ps -a
# kill xxx
复制
如下可以采用脚本运行:
# cat kill_qemu.sh
#! /bin/sh
ps -a | grep qemu-system-arm | awk '{print $1}' | xargs sudo kill -9
复制
本文讲述了Qemu环境启动Linux内核,并挂载SD卡中的根文件系统的一些操作步骤。如果需要在Qemu环境下以ARM开发板的正常启动流程来加载Linux内核并挂载根文件系统,可以参考下一篇文章《Qemu搭建ARM vexpress开发环境(二)----通过u-boot启动Linux内核》。