最开始接触ARM-Linux平台下开发时,用的还是ARM7/ARM9之类的处理器,32位的CPU,系统运行在norFlash+nandFlash组合的存储上。由于ARM7/ARM9的处理器性能偏弱,而且配套的存储空间一般也不够大,所以大多数情况下都采用交叉编译开发的方式,具体实现的方式就是在x86-Linux平台下,下载安装一个交叉编译器。Ubuntu系统下安装交叉编译器可以通过apt命令在线安装,安装方式如下:
# 针对32位的ARM平台
sudo apt-get install g++-arm-linux-gnueabihf
# 针对64位的ARM平台
sudo apt-get install g++-aarch64-linux-gnu
交叉编译器安装完成后,用相对应的交叉编译命令来编译程序。如下图所示,x86-Linux平台下,编译本平台的程序,用gcc命令编译,而编译arm平台下的程序,则用arm-linux-gnueabihf-gcc命令。
用file命令可以看出,编译生成的程序因编译器的不同而不同,在本机编译之后生成的文件是x86-64平台可以运行的,而用交叉编译器编译生成的则是ARM平台可以运行的。这里要注意的不同平台的文件需要在它对应的平台运行,如x86-64只能在x86-64架构CPU的系统上运行,而ARM则只能在arm架构CPU的系统上运行了,平台不匹配就会报错,如下图所示。
上面编译生成hello-arm文件的过程就是交叉编译,这样做的好处是可以利用x86架构CPU强大的性能和便利的开发环境,在编译大型软件库的时候,可以更快的进行编译。但这种方式也有着自己的缺点,比如需要手动配置环境变量、编译软件的时候要添加额外的参数、编译生成的程序没法直接在编译环境运行、调试环境不好搭建等,此外,随着软件库功能的逐渐丰富,它的依赖关系也越发复杂,直接用交叉编译工具的话,解决编译时依赖和运行时依赖也是一个比较麻烦的过程。
好在随着ARM处理器的不断发展,在64位指令集的引入和安卓系统不断对它性能要求的提高下,以及ARM-Linux生态环境的不断完善下,基于ARM64架构CPU的主机问世了,凭借着完善的系统运行库以及开发工具的支持,直接在ARM主机上进行开发(直接编译)也逐渐的方便了起来。
可即使这样,也难免有需要用到交叉编译环境的时候,毕竟基本还是用x86主机进行办公的,在ARM主机不太方便直接使用的情况下,用x86主机+虚拟机的形式搭建的交叉编译环境来编译调试程序也是个不错的选择。此外,qemu+chroot的方式提供了在交叉编译环境下解决库依赖问题的良好方案,那么接下来就讲下这种交叉编译环境搭建的过程。
虚拟机软件这里选择VMware,可选的还有Oracle的VirtualBox。VWware安装过程自不必多说,接下来要安装一个Linux的系统镜像,这里推荐用Debian和Ubuntu,两个系统都是用apt命令做软件包安装管理的,使用起来比较类似。为了使得系统更轻量级这里就以Debian9系统为例进行说明,首先到Debian官方下载一个系统安装镜像,推荐下载“debian-9.4.0-amd64-netinst.iso”网络安装镜像,体积小,下载快。接着就是用VMware安装虚拟机系统的过程,这个也没啥好讲的,在VMware软件界面选择文件->新建虚拟机,打开新建虚拟机向导界面,选择典型安装,根据提示,一路Next就行了。
安装完成之后进入系统,Debian9默认桌面环境如下图所示,是一个xfce4的轻量级桌面环境。对于交叉编译开发环境来说,图形界面是没有必要的,所以为了使得后续打包镜像体积尽可能小,还可以选择不安装桌面环境。不过对于普通用户来说,带桌面化的图形界面还是好用一些。
带有图形化界面的虚拟机系统,建议安装vmware-tools工具,这样可以支持配置虚拟机系统全屏显示,也方便了与外部系统文件共享。安装命令很简单,用apt命令在线安装即可。
sudo apt update
sudo apt install open-vm-tools open-vm-tools-desktop
第一章有写到,直接使用交叉编译工具的话,不好解决程序的编译时依赖和运行时依赖。比如我编译一个Qt的应用程序,如果是直接在ARM主机上开发,那就直接编译运行程序就行了。而当在X86主机上使用交叉编译工具的时候,则还要先用交叉编译工具编译一个Qt的库(费时),然后配置该库相关的环境变量,编译完程序后也没法直接运行验证,还需要将编译后的可执行文件拷贝到ARM主机(假设使用的是ARM交叉编译工具)上,而且ARM主机上的库版本还要保证与X86主机上编译的一致,这样最终才能运行。
如何使得交叉编译环境与目标运行环境尽可能一致,而不用配置软件库的编译时/运行时依赖呢?最好的方法自然是将目标运行环境的根文件系统直接拷贝过来,然后用qemu模拟在根文件系统中直接编译/运行程序,这样编译时的环境跟最终运行到目标主机上的环境完全一致,就避开了配置库依赖的麻烦。
这里以ARM平台为例,目标根文件系统就是指ARM主机系统上挂载在“/”目录下的根文件系统,也是指系统root分区,它里面包含了整个系统的软件和库文件。直接到ARM主机上将它的根文件系统打包拷贝过来是可以的,不过一般文件比较多,需要做一些裁剪操作。我们这里选择从ARM平台的系统安装镜像中来拷贝这个根文件系统,选用FT1500a处理器(ARM64兼容)适配的Kylin4.0.2-sp2系统,系统安装镜像如下图所示,其它平台亦可以此为参考。
在Kylin4.0.2-sp2系统镜像中的casper目录下,可以找到一个filesystem.squshfs文件,它是一个liveOS镜像文件。liveOS是在系统安装时,运行的系统,比如在安装ubuntu/debian系统时,启动后在grub菜单可以选择“试用系统”,之后进入的就是liveOS系统。liveOS系统一般跟安装后的系统差异不大,是一个精简版,比如centos系统安装盘,也有为了使得系统试用功能比较丰富,liveOS镜像做的比较大,比如ubuntu/kylin系统的安装盘。将filesystem.squashfs文件拷贝到我们第二章安装的虚拟机系统中的/opt目录下,然后用unsquash命令把它给进行解压。
sudo apt install squashfs-tools
sudo unsquashfs filesystem.squashfs
解压后,能看到在当前文件夹新增了一个squashfs-root的目录,cd到该目录下,查看该目录下的文件,如linux系统的根目录下一致。
在前一章已经拷贝了目标根文件系统,可是里面的程序文件是无法直接运行的,因为虚拟机系统是x86平台的,需要借助qemu工具才能运行其它平台的程序。接下来就需要安装qemu工具,安装配置方式也很简单,直接通过apt命令在线安装,然后拷贝所需文件到目标根文件系统即可。
sudo apt install qemu-user-static
sudo cp /usr/bin/qemu-aarch64-static /opt/squashfs-root/usr/bin/
sudo cp /usr/bin/qemu-arm-static /opt/squashfs-root/usr/bin/
拷贝完后,在切换到交叉编译环境前,先看下当前终端的运行环境,从编译器版本和可执行文件格式,可以看出当前终端运行环境为x86_64平台。
接下来需要挂载并bind下dev、proc和sys目录,chroot到目标根文件系统中,就进入到了交叉编译环境。操作步骤如下:
sudo mount -t proc /proc /opt/squashfs-root/proc
sudo mount -t sysfs /sys /opt/squashfs-root/sys
sudo mount -o bind /dev /opt/squashfs-root/dev
sudo mount -o bind /run /opt/squashfs-root/run
sudo chroot /opt/squashfs-root/
执行完chroot命令后,终端就切换到了ARM64的执行环境中了,用gcc -v命令查看编译器版本,已经变成了ARM平台下的编译器,同时系统路径下可执行文件也是ARM平台格式的了。
这个时候,已经可以进行程序的开发编译了,同时,编译的程序也可以直接运行验证,编译内核或者驱动模块也是可以的,不过驱动就没法直接加载验证了。如果想退出交叉编译环境,直接输入exit命令即可,再次要进入重新挂载并执行chroot命令。
为了使得切换到交叉编译环境更加,方便,可以写一个脚本,需要切换执行这个脚本就行。脚本内容如下:
#!/bin/bash
ROOT_PATH=/opt/squashfs-root/
sudo umount ${ROOT_PATH}proc
sudo umount ${ROOT_PATH}sys
sudo umount ${ROOT_PATH}dev
sudo umount ${ROOT_PATH}run
sudo mount -t proc /proc ${ROOT_PATH}proc
sudo mount -t sysfs /sys ${ROOT_PATH}sys
sudo mount -o bind /dev ${ROOT_PATH}dev
sudo mount -o bind /run ${ROOT_PATH}run
sudo chroot ${ROOT_PATH}
同时为了使得虚拟机系统的终端环境与交叉编译环境区分开,可以设置下终端的字符颜色。设置方式为修改"/opt/squashfs-root/root/.bashrc"文件,在文件最后添加如下内容:
export PS1="\[\e[32m\][\[\e[m\]\[\e[31m\]\u\[\e[m\]\[\e[33m\]@\[\e[m\]\[\e[32m\]arm64\[\e[m\] \@ \[\e[36m\]\w\[\e[m\]\[\e[32m\]]\[\e[m\]\[\e[32;47m\]\\$\[\e[m\] "
echo "Warning, Now you are in arm64 environment, you can use the 'exit' command to logout."
echo "Use 'gcc -v' to detect it."
这样切换到交叉编译环境后,终端显示就如下图所示。
在交叉编译环境下,也可以通过apt命令安装所需要的依赖包,配置网络和配置软件源跟直接在ARM主机上配置也没有什么差异,如安装qt5-default包:
使用虚拟机镜像的一个好处在于比较方便打包,在另外一个电脑上使用时直接把文件拷贝过去就能直接使用了,省去了重复安装系统的麻烦。虚拟机镜像一般比较大,可以用一些方法进行裁剪压缩,下面简单介绍下。
交叉编译环境下,并不是所有软件都可以直接运行,比如图形相关的,依赖Xorg图形库的应用不能直接运行。这是因为切换进入交叉编译环境默认用的是/bin/bash命令进行shell登陆,并没有启动图形服务。实际测试图形界面虽然可以运行,但有些功能使用不了,如鼠标/键盘的交互等,从调试日志看是qemu并不能完整的翻译一些指令导致的。
因此交叉编译环境下,可以通过apt命令进行一些使用不到的包的卸载,比如桌面软件、图形库相关软件、字库等,以下是参考命令。
apt remove firefox wps-office sogou* junyun* linkdood* smplayer mate-desktop xorg* xserver-*
rm /lib/firmware/* -rf # 注意在交叉编译环境下执行
apt remove
apt autoremove
apt autoclean
当输入exit命令退出交叉编译环境,切换到虚拟机系统x86平台环境下后,裁剪方式与交叉编译环境并无太大差异,也就是卸载浏览器、播放器等用不到的软件,甚至桌面用不到的话也可以卸载了,最后不要忘了执行下"apt clean"清理下包管理缓存。
在打包前,需要先卸载下交叉编译环境挂载的目录,命令如下:
sudo umount /opt/squashfs-root/proc
sudo umount /opt/squashfs-root/sys
sudo umount /opt/squashfs-root/dev
sudo umount /opt/squashfs-root/run
然后用vm-tools工具进行磁盘碎片整理,进一步缩小虚拟机镜像占用空间:
sudo vmware-toolbox-cmd disk shrink /
sudo vmware-toolbox-cmd disk shrinkonly
最后就可以关机了,打开电脑上的文件浏览器,找到虚拟机镜像的目录,把整个目录压缩打包就行了。