本系列将带你从环境搭建开始,系统掌握嵌入式Linux应用层开发。所有代码在ARM开发板上实测通过,请放心复制使用。
一、为什么需要交叉编译?
嵌入式设备(如I.MX6ULL、树莓派等)通常资源受限,难以安装完整的编译工具链。我们的开发流程通常是:
在x86 PC上编写代码 → 通过交叉工具链生成ARM架构程序 → 下载到开发板运行 。
本篇就以最常见的arm-linux-gnueabihf-工具链为例,完成环境搭建、Hello World运行以及一份"一劳永逸"的通用Makefile。
二、安装交叉编译工具链
2.1 方式一:通过APT直接安装(Ubuntu/Debian推荐)
bash
sudo apt update
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
安装完成后,工具链会存放在/usr/bin/下,可直接使用。
2.2 方式二:手动下载Linaro工具链(通用)
若需特定版本或APT版本不兼容,可从Linaro官网下载:
bash
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt
然后配置环境变量:
bash
echo 'export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc
2.3 验证安装
bash
arm-linux-gnueabihf-gcc -v
正常输出编译器版本信息即表示安装成功。
三、第一个程序:Hello World
3.1 编写hello.c
c
#include <stdio.h>
int main(void)
{
printf("Hello, ARM Linux!\n");
return 0;
}
3.2 交叉编译
bash
arm-linux-gnueabihf-gcc -o hello hello.c
3.3 检查生成的文件
bash
file hello
# 输出示例: hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked ...
ARM标识说明它是ARM架构程序。
3.4 部署到开发板
假设开发板IP为192.168.1.100,使用scp传输:
bash
scp hello root@192.168.1.100:/root/
在开发板终端执行:
bash
chmod +x /root/hello
./hello
若提示-sh: ./hello: not found或缺少动态库,通常是因为开发板上缺少标准C库。解决方法:
- 使用静态链接 重新编译:
arm-linux-gnueabihf-gcc -static -o hello hello.c,生成的程序会包含所需库,体积稍大但独立运行。 - 或将工具链中的动态库(如
libc.so.6)复制到开发板/lib目录。
四、编写通用Makefile模板
实际项目源文件众多,手动编译不可取。下面从零开始构建一个"万能"Makefile。
4.1 最简版本
makefile
hello: hello.c
arm-linux-gnueabihf-gcc -o hello hello.c
缺点:每增加一个.c文件都要修改。
4.2 使用变量和自动规则
makefile
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
TARGET = hello
SRCS = $(wildcard *.c) # 自动查找当前目录所有.c文件
OBJS = $(SRCS:.c=.o) # 将.c后缀替换为.o
$(TARGET): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -c -o $@ $<
说明:
$@:目标文件$^:所有依赖文件$<:第一个依赖文件wildcard函数:展开通配符SRCS:.c=.o:替换后缀
现在,添加新.c文件时完全无需修改Makefile。
4.3 加入编译选项与清理功能
makefile
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip
TARGET = hello
CFLAGS = -Wall -g -O2
LDFLAGS =
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) $(TARGET)
重要提醒 :Makefile中的命令行(如
$(CC) -o $@ $^)前必须使用Tab键缩进,不能用空格。如果在博客中复制代码,请手动检查或使用IDE转换。
4.4 进阶:支持多目录与静态库(可选)
当项目结构更复杂时,可参考以下扩展:
makefile
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
CFLAGS = -Wall -g -O2
SRCDIR = src
INCDIR = include
LIBDIR = lib
BINDIR = bin
# 生成静态库
LIB = $(LIBDIR)/libfoo.a
LIB_SRCS = $(wildcard $(SRCDIR)/*.c)
LIB_OBJS = $(LIB_SRCS:.c=.o)
.PHONY: all clean
all: $(LIB) app
$(LIB): $(LIB_OBJS)
$(AR) rcs $@ $^
# 主程序(假设main.c在根目录)
app: main.c $(LIB)
$(CC) $(CFLAGS) -I$(INCDIR) -L$(LIBDIR) -o $(BINDIR)/app main.c -lfoo
clean:
rm -f $(LIB_OBJS) $(LIB) $(BINDIR)/app
这套模板稍加修改就能适配大多数嵌入式项目。
五、系列连载计划
本文将开启一个完整的嵌入式Linux应用开发系列,后续会依次覆盖:
- ✅ 环境搭建与Makefile(本篇)
- 文件IO系统调用:open/read/write/lseek 深入实验
- 标准IO与缓冲区陷阱:fopen/fwrite/setvbuf 内核行为验证
- 进程管理:fork/vfork 写时拷贝实测
- 进程间通信:管道、共享内存、信号
- 多线程同步:互斥锁、条件变量、线程池
- 网络编程:Socket、epoll、高并发服务器
- 综合项目:智能家居网关(串口+网络+数据库)
建议收藏本系列,获取后续更新。
六、总结与思考题
我们从交叉编译的概念出发,完成了工具链安装、Hello World在ARM板上的运行,并打磨出一份"一次编写,随处可用"的通用Makefile。这是嵌入式Linux开发的第一块基石。
思考题 :
如果某源文件调用了线程库pthread_create,链接时需要加上-lpthread。请思考:这个选项应该放在Makefile的CFLAGS还是LDFLAGS中?为什么?欢迎在评论区留下你的答案。
参考资料
- Linaro Toolchain Releases
- GNU Make Manual
man arm-linux-gnueabihf-gcc
