一、嵌入式 Linux 概述:从裸机到 AI 推理的必经之路
做嵌入式 Linux 开发这些年,我最大的感触是——看懂硬件,才能用好软件。尤其当我们要在板子上跑 llama.cpp 这种大模型推理时,底层平台的理解直接决定了性能优化空间。
这一章我们先把地基打牢。讲三个东西:嵌入式系统跟 Linux 内核怎么配合的、主流硬件平台长什么样、交叉编译到底在干什么。都是老生常谈的内容,但我用自己踩过的坑来串一遍,你听起来会更实在。
1.1 嵌入式系统 vs Linux 内核
嵌入式系统说白了就是为特定任务设计的计算机系统。和 PC 不同,资源有限、功耗敏感、实时性要求高。我在 2016 年做一款工业相机时,跑的是 Linux 内核,但因为中断响应慢了 2 毫秒,图像采集就丢帧。嗯,那时候我才真正理解「内核裁剪」不是炫技,是刚需。
Linux 内核在嵌入式环境中的作用包括:
- 进程调度 — 多任务切换,保证实时任务优先
- 内存管理 — 物理内存和虚拟地址映射,管理有限的 DRAM
- 设备驱动 — 统一硬件抽象,比如 GPIO、I2C、SPI、UART
- 文件系统 — ext4、ubifs、squashfs 等适应不同存储介质
- 网络协议栈 — TCP/IP、Socket,推理结果外传的基础
你想想看,llama.cpp 推理时要用到多线程(pthread)、大内存(mmap)、甚至 GPU 加速(如果板子有),这些全都依赖内核提供的基础设施。如果内核编译时没开 CONFIG_SMP,那就没办法利用多核 CPU 并行计算,推理速度直接腰斩。我之前在 RK3588 板上就犯过这个错,编译内核时没注意符号表,结果推理速度只有 3 token/s,后来加上 SMP 才跑到 12 token/s。
关键一句话:嵌入式 Linux 不是把桌面 Linux 的直接缩小版,而是需要针对硬件做剪裁和优化的定制系统。跑 llama.cpp 之前,先确认你的内核配置是否合适。
1.2 常见嵌入式硬件平台
现在主流的嵌入式 Linux 平台按指令集分两大阵营:ARM64 和 RISC-V。其他像 x86 的 Atom 也有一席之地,但今天我们只谈两个最热门的。
1.2.1 ARM64(AArch64)
ARM64 是目前嵌入式 Linux 的绝对主力。手机、路由器、工业控制、AI 边缘盒子,十有八九是这颗芯。优点在于:
- 生态极其成熟,主线内核支持度高
- 有 NEON 向量指令集,对矩阵运算友好(llama.cpp 的 GGML 就用到 NEON)
- 各种 SoC,从低端 Cortex-A53 到高端 Cortex-X1,覆盖广泛
我个人习惯在 RK3588(ARM Cortex-A76 + A55)上做原型验证,性能接近入门级笔记本,而且 NPU 可以用来做部分算子加速。不过要注意,不同厂家对内核版本维护力度不一样,有的只给 Linux 4.19 长期支持,你想跑最新的 llama.cpp 就得自己 backport 一些补丁。
1.2.2 RISC-V
RISC-V 是后起之秀,开源指令集架构,这几年国内很多厂家都在推(比如全志 D1、平头哥 C910)。它的优势是:
- 完全开放,没有授权费
- 指令集简洁,硬件实现简单
- 可自定义扩展指令(比如向量扩展 V 扩展)
但是生态还比较弱。我曾经在一个 RISC-V 开发板上尝试编译 llama.cpp,发现 GCC 工具链的浮点优化还没 ARM 成熟,同样的模型在 ARM64 上能跑 8 token/s,RISC-V 上只有 2 token/s。这不是架构的问题,是软件栈的时间投入问题。相信再过两三年会好很多。
选平台建议:如果你刚入门,优先选 ARM64 的板子(比如树莓派 4/5、RK3588 开发板),工具链齐全,社区资料多。等 RISC-V 生态更完善后再迁移不迟。
1.3 交叉编译概念
交叉编译就是在 宿主机(比如你的 x86 PC) 上编译出能在 目标机(ARM64 或 RISC-V 板子) 上运行的可执行文件。为什么要这样做?因为板子资源有限,跑个完整的 GCC 编译器内存就不够了。
我刚开始做嵌入式那会,不懂交叉编译,直接在板子上执行 gcc hello.c -o hello,编译一个 50 行的程序花了 3 分钟。后来我学会了用交叉编译链,在 PC 上 5 秒搞定。嗯,效率差距就是这么大。
交叉编译的核心部件:
- 交叉工具链 — 比如
aarch64-linux-gnu-gcc,专门生成 ARM64 指令 - sysroot — 目标板上的根文件系统的拷贝,包含标准库和头文件
- CMake / Makefile — 传递交叉编译标志,指定工具链路径
举个例子,编译一个简单的 C 程序:
# 在 x86 PC 上
aarch64-linux-gnu-gcc -o hello_arm hello.c
file hello_arm
# 输出: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)
# 然后把 hello_arm 拷贝到板子上运行
scp hello_arm root@192.168.1.100:/tmp/
ssh root@192.168.1.100 /tmp/hello_arm
对于 llama.cpp 这种大型项目,一般用 CMake 指定交叉编译链:
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-arm64.cmake ..
make -j$(nproc)
其中 toolchain-arm64.cmake 内容大致是:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH /path/to/sysroot)
我曾经遇到的坑:交叉编译 llama.cpp 时忘记指定 OpenBLAS 的交叉版本,结果链接了 x86 的库文件,板子上运行直接段错误。后来我强制用 CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER 才解决。一定要确保所有依赖库都是交叉编译的,不能混用。
本章知识体系
下面这张图把本章三个核心概念串起来,帮你建立整体认知:
这张图其实也是我自己的实践路线。先把系统概念理清,再选平台,然后搞定交叉编译,最后才能顺利把 llama.cpp 跑起来。下一步我们会深入到具体的软硬件选型,以及搭建第一个宿主机-目标机开发环境。