树莓派学习笔记(五):交叉编译
交叉编译
交叉编译的概念
所谓编译,是指根据编辑好的源代码,生成本平台可直接执行的文件(比如gcc编译C语言,默认生成的a.out
,使用./a.out
即可运行),这种方式适用于本平台有足够的资源和条件时使用。
但当目标平台没有充足的资源,无法安装、运行编译工具,甚至操作系统时,我们必须先在有条件的平台上进行编译,将可执行文件复制到目标平台运行。
比如C51单片机内存极小,根本无法运行操作系统,更别提安装编译器了。其主板运行的机器指令等,也和windows不同。质我们总是在windows平台上的IDE:Keil中编写源代码,编译生成hex
文件,再通过stc-isp
工具将其下载(烧写)到单片机上。
那么,树莓派内存空间大,可以运行Ubuntu、Debian、Android甚至windows,也需要交叉编译吗?
答案是肯定的。要知道,操作系统本身也是软件,也需要源代码编译生成。当工程师刚刚设计好硬件部分,此时树莓派还没有运行操作系统的能力。一个平台/主机的运行,至少需要两样东西:bootloader
和操作系统核心。因此要进行产品测试,编译适合树莓派运行,适配树莓派硬件特点的操作系统版本后,产品才能发布上市。
交叉编译涉及到的两个基本对象:宿主机(host)和目标机(target),其中
- 宿主机指编辑和编译程序的平台,一般是基于X86的PC机,通常也被称为主机。
- 目标机是用户开发的系统,通常都是非X86平台。host编译得到的可执行代码在target上运行。
交叉编译工具链的安装
不同平台使用的指令集不同,交叉编译工具链也就不同。因此目标平台是什么,在宿主机平台上编译的时候就要选择正确(比如C51课程阶段Keil创建工程时就要选择目标设备为AT89C52)

树莓派平台的交叉编译,由树莓派官方提供了工具链,源码地址:raspberry-tools
。通过git clone到本地(宿主机,编译的机器,这里我用的是另一台ubuntu64位系统的ECS)后,进入如下目录:tools-master/arm-bcm2708
,
如果宿主机是64位版本选择gcc-linaro-arm-linux-gnueabihf-raspbian-x64
,如果是32位选择gcc-linaro-arm-linux-gnueabihf-raspbian
。
以64位系统为例,其提供的gcc程序为bin/arm-linux-gnueabihf-gcc-4.8.3
,
我们使用tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-gcc-4.8.3
即可调用4.8.3版本的的gcc进行编译
如果嫌上面的路径太长太深,不愿意一步一步进到具体路径,可以设置环境变量:
修改环境变量:
export PATH=$PATH:上面的gcc程序路径写到bin
,便可直接在任意目录使用arm-linux-gnuabihf-gcc
命令:echo export PATH=$PATH >> ~/.bashrc;source ~/.bashrc
,将临时的环境变量写入文件这样就不会丢失
交叉编译小实例:基于socket
的聊天应用编译
在Linux系统编程课程阶段,我们学习了网络编程,这里以基于socket实现的聊天服务器(及其客户端)为例,在宿主机(ubuntu18.04)上交叉编译可在树莓派运行的可执行文件
首先是源码,服务端:
点击展开
1 |
|
客户端:
点击展开
1 |
|

可以看到,两种gcc编译结果不同,使用arm-linux-gnueabihf-gcc
编译得到的可执行文件目标平台为arm,才能在树莓派上运行。
接着在树莓派上使用scp命令将编译好的可执行文件下载到开发板,并运行:
1 |
|

运行效果如上所示
带wiringPi的交叉编译
前面在树莓派上利用wiringPi库开发过一些应用,那么假如我们树莓派的操作系统还没有准备好,就只能在宿主机上提前编译好。wiringPi也是如此,之前在树莓派上下载了wiringPi源码,并通过gcc,build了适合树莓派的特制版本,宿主机上要想获取wiringPi库进行相应外设开发,同样也需要下载源码、使用gcc进行build。
这就导致一个问题:树莓派上的gcc和宿主机上的gcc目标平台并不相同,宿主机上的gcc适用于x86平台,编译出的wiringPi库自然也只能用于x86的操作系统,那还怎么进行下一步的交叉编译呢?
wiringPi
的Makefile
,指定gcc为编译器:

树莓派的gcc,目标平台是arm64:

宿主机编译安装的wiringPi,目标平台是x86-64:

如果强行使用x86平台的wiringPi进行交叉编译,会报错(即使强行去修改Makefile中的gcc,编译出来的版本依然不能用):

那这种情况下有两种思路:
第一种,“就地取材”,既然我们之前已经编译好了树莓派能用的wiringPi,直接将其拿到宿主机去用即可
- 树莓派上执行
scp /usr/lib/libWiringPi.so.xxx user@host:target-path
,将动态库拷贝到宿主机- 可以使用
ln -s target llinkname
的格式创建一个软链接,方便链接时名字的简洁。关于硬链接与软链接的概念和区别具体参考https://www.cnblogs.com/zhangna1998517/p/11347364.html,主要有以下几点: - 软链接是”真正的“链接,类似于一个不占磁盘空间的指针,只存放位置信息;硬链接像目标文件的备份,内容随目标文件变化而变化,相比软链接可以防止用户误删文件。硬链接通过索引节点实现,只有当文件及其所有硬链接都被删除,相应的磁盘空间才会被释放。
- 可以使用
- 在宿主机上使用
arm-linux-gnueabihf-gcc file.c -I path-to-wiringPi -Lpath-to-library -lwiringPi -o target
的命令进行交叉编译 - 编译好后使用方式和上面socket聊天应用一样,复制到开发板直接运行即可。
第二种方式,更加彻底、符合常规:那就是在安装外设库的时候配置目标平台,但由于这个wiringPi并没有支持配置,因此没有展示
