banner
NEWS LETTER

嵌入式 Linux 从入门到开发:一条可落地的实践路线

Scroll down

学习嵌入式 Linux 最容易遇到的问题,不是资料太少,而是资料太散。今天看驱动,明天看 Buildroot,后天又去研究 U-Boot,结果每个概念都知道一点,但真正拿到一块板子时,还是不知道从哪里开始开发。

更合理的方式,是按真实项目的开发链路学习:先能把系统跑起来,再能改 rootfs,再能开发应用,最后再深入内核和驱动。

系列目录

这篇文章作为总览,后续每个方向拆成独立文章继续展开:

一、先建立整体地图

一个典型嵌入式 Linux 系统可以拆成几层:

1
2
3
4
5
6
7
8
9
10
11
硬件
|
BootROM
|
Bootloader, 例如 U-Boot
|
Linux Kernel + Device Tree
|
RootFS
|
系统服务和业务应用

从开发角度看,对应的工作是:

  • 能烧录镜像。
  • 能看串口日志。
  • 能进 shell。
  • 能改启动脚本。
  • 能交叉编译应用。
  • 能打包 rootfs。
  • 能配置内核和设备树。
  • 能调试应用、服务、驱动和启动问题。

新手不要一开始就钻进驱动源码。驱动当然重要,但如果连系统启动流程、rootfs 结构、交叉编译和日志排查都不熟,直接看驱动会非常吃力。

二、第一阶段:Linux 基础要够用

嵌入式 Linux 开发离不开基础命令,但不需要先把所有命令背完。

优先掌握这些:

1
2
3
4
5
6
7
8
9
10
11
12
13
ls
cat
grep
find
ps
top
df
du
mount
ifconfig
ip
dmesg
journalctl

在精简 rootfs 里可能没有完整 GNU 工具,很多命令来自 BusyBox。BusyBox 参数比桌面发行版少,脚本写法要保守一点。

必须理解几个目录:

1
2
3
4
5
6
7
8
9
10
/bin        基础命令
/sbin 系统管理命令
/etc 配置文件
/dev 设备节点
/proc 内核导出的运行时信息
/sys 设备和驱动模型
/lib 动态库
/usr 用户态程序和资源
/tmp 临时文件
/var 日志和运行状态

如果目标系统只读,就要额外关注哪些目录挂载到了可写分区。例如配置可能放在 /data/config,日志可能放在 /data/log

三、第二阶段:C 语言和交叉编译

嵌入式 Linux 上的底层应用大多仍然离不开 C/C++。

最低要求是:

  • 能读懂指针、结构体、函数指针。
  • 能处理文件 IO。
  • 能处理 socket。
  • 能写多线程程序。
  • 能理解动态库和静态库。
  • 能用 Makefile 或 CMake 构建项目。

交叉编译是入门时必须过的一关。主机通常是 x86_64,目标设备可能是 ARM、AArch64、MIPS 或 RISC-V。

一个最小示例:

1
2
3
4
5
6
7
#include <stdio.h>

int main(void)
{
printf("hello embedded linux\n");
return 0;
}

交叉编译:

1
2
arm-linux-gnueabihf-gcc hello.c -o hello
file hello

拷到板子运行:

1
2
scp hello root@192.168.1.100:/tmp/
ssh root@192.168.1.100 /tmp/hello

如果运行时报:

1
No such file or directory

不一定是文件不存在,也可能是动态链接器路径不匹配。可以用:

1
readelf -l hello | grep interpreter

检查目标 rootfs 是否有对应的动态链接器。

四、第三阶段:把板子启动起来

开发板到手后,第一件事不是改代码,而是把启动链路跑通。

需要准备:

  • 串口工具。
  • 烧录工具。
  • 官方 SDK 或镜像。
  • 网线或 USB 网络。
  • TFTP/NFS 可选。
  • 原理图和芯片手册可选。

串口日志非常关键。启动失败时,它通常是唯一可靠的信息来源。

启动日志里重点看:

1
2
3
4
5
6
7
U-Boot 版本
DRAM 初始化
Kernel 加载地址
Device Tree 加载地址
Kernel command line
RootFS 挂载方式
init 进程是否启动

如果内核起来了但进不了 shell,常见原因是:

  • rootfs 路径错误。
  • init 程序不存在。
  • 动态库缺失。
  • 设备节点缺失。
  • 文件系统损坏。
  • 内核参数 root= 配错。

这一步的目标很明确:能稳定进入系统 shell。

五、第四阶段:理解 RootFS

RootFS 是用户态运行的基础。很多嵌入式问题表面看像应用问题,本质上是 rootfs 裁剪或配置问题。

需要掌握:

  • BusyBox 怎么配置。
  • /etc/inittab 或启动脚本怎么执行。
  • /dev 是静态节点还是 mdev/udev 生成。
  • 动态库放在哪里。
  • 应用如何开机启动。
  • 配置如何持久化。
  • 日志写到哪里。
  • 系统是只读 rootfs 还是可写 rootfs。

例如只读 rootfs 里动态设置时区,就不能直接写 /etc/localtime,需要把配置保存到 /data/config/timezone,启动时再恢复 bind mount 或 TZ

判断挂载情况:

1
2
3
mount
df -h
cat /proc/mounts

一个可靠的嵌入式系统,通常会把系统镜像和用户数据分开:

1
2
3
rootfs: 只读,随固件升级
data: 可写,保存配置、日志、运行状态
tmpfs: 临时文件,重启丢失

六、第五阶段:用 Buildroot 或 Yocto 构建系统

手工拼 rootfs 可以帮助理解原理,但真实项目更推荐用构建系统。

Buildroot 适合:

  • 系统相对简单。
  • 想快速生成工具链、内核、rootfs。
  • 团队希望配置简单。
  • 产品包数量不算特别复杂。

Yocto 适合:

  • 产品线复杂。
  • 需要精细的软件包管理。
  • 多平台、多版本长期维护。
  • 对许可证、组件追踪要求高。

入门阶段建议先用 Buildroot。它的学习路径更短,能让你更快看到完整系统。

Buildroot 常见目录:

1
2
3
4
5
6
configs/                板级默认配置
package/ 软件包定义
board/<vendor>/<board>/ 板级脚本和 overlay
output/images/ 最终镜像
output/target/ rootfs 内容
output/host/ 交叉工具链

常见操作:

1
2
3
4
make menuconfig
make linux-menuconfig
make busybox-menuconfig
make

项目里不要只保存最终镜像,更要保存配置文件和构建脚本,否则后续版本很难复现。

七、第六阶段:开发系统服务和业务应用

大多数嵌入式 Linux 开发岗位,日常工作并不是天天写驱动,而是开发设备上的系统服务和业务应用。

常见任务包括:

  • 网络配置。
  • MQTT/HTTP 通信。
  • OTA 升级。
  • 日志采集。
  • 设备参数管理。
  • 本地数据库或配置文件管理。
  • 外设控制。
  • 看门狗喂狗。
  • 异常自恢复。

服务开发要关注几个问题:

  • 进程如何启动。
  • 崩溃后是否自动重启。
  • 配置变更如何生效。
  • 日志是否可追踪。
  • 网络断开后如何重连。
  • 写 flash 是否有频率控制。
  • 异常断电后配置是否损坏。

一个服务启动脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh

APP=/usr/bin/device_agent
CONF=/data/config/device_agent.conf
LOG=/data/log/device_agent.log

mkdir -p /data/log

while true; do
"$APP" -c "$CONF" >> "$LOG" 2>&1
sleep 2
done

这不是最终的完美方案,但能说明一个嵌入式服务最基本的诉求:能启动、能记录日志、异常退出后能回来。

八、第七阶段:再深入 Kernel 和驱动

当你能稳定构建系统、开发应用、修改 rootfs 后,再深入内核和驱动会顺很多。

驱动学习建议按外设类型推进:

  • GPIO。
  • UART。
  • I2C。
  • SPI。
  • PWM。
  • ADC。
  • USB。
  • Ethernet。
  • Display。

同时要理解 Device Tree:

1
2
3
4
5
6
7
8
&i2c1 {
status = "okay";

sensor@48 {
compatible = "vendor,temp-sensor";
reg = <0x48>;
};
};

设备树不是驱动本身,而是描述硬件连接关系。驱动根据 compatible、reg、interrupts、clocks、gpios 等信息找到设备并完成初始化。

驱动调试常用工具:

1
2
3
4
5
6
7
8
9
dmesg
cat /proc/interrupts
ls /sys/bus
ls /sys/class
hexdump
i2cdetect
i2cget
i2cset
devmem

驱动问题不要只看代码。原理图、引脚复用、电源时序、时钟、复位脚、设备树配置都可能是根因。

九、第八阶段:学会调试和定位问题

嵌入式开发真正拉开差距的地方,是定位问题的能力。

常见问题和工具:

  • 启动失败:串口日志、U-Boot 环境变量、kernel cmdline。
  • 应用崩溃:core dump、gdb、addr2line。
  • 内存泄漏:top、smem、pmap、valgrind 可选。
  • CPU 占用高:top、perf、strace。
  • 文件系统异常:dmesg、fsck、mount。
  • 网络异常:ping、tcpdump、ip route、iptables。
  • 驱动异常:dmesg、sysfs、procfs、逻辑分析仪。

strace 对用户态排查非常有用:

1
strace -f -o app.strace /usr/bin/my_app

它可以看到应用打开了哪些文件、访问了哪些设备、连接了哪些 socket、在哪个系统调用上失败。

如果设备空间紧张,可以在调试版本里临时打开这些工具,量产版本再裁剪。

十、第九阶段:形成工程闭环

能写代码只是开始,真正可交付的嵌入式系统还需要工程闭环。

至少要有:

  • 固件版本号。
  • 构建脚本。
  • 可复现构建环境。
  • OTA 升级方案。
  • 回滚策略。
  • 出厂默认配置。
  • 日志导出方式。
  • 故障自恢复机制。
  • 看门狗策略。
  • 基础自动化测试。

一个简单的版本文件:

1
2
/etc/os-release
/etc/firmware_version

应用启动时打印版本:

1
2
3
device_agent version: 1.4.2
build time: 2026-06-26 21:36:28
git commit: abcdef0

现场设备出问题时,版本信息、日志和配置快照往往比代码本身更重要。

十一、一条推荐学习路线

可以按下面顺序推进:

  1. 熟悉 Linux 命令和目录结构。
  2. 学会 C 语言、Makefile、交叉编译。
  3. 跑通开发板官方镜像。
  4. 看懂 U-Boot、Kernel、RootFS 的启动日志。
  5. 用 Buildroot 生成一个最小系统。
  6. 修改 rootfs overlay,加入启动脚本和配置文件。
  7. 写一个能开机启动的业务服务。
  8. 加入 MQTT、日志、配置持久化、看门狗。
  9. 学会用 gdb、strace、tcpdump、dmesg 定位问题。
  10. 再深入设备树和常见外设驱动。
  11. 最后补齐 OTA、回滚、量产配置和自动化测试。

这条路线的核心是先跑通完整链路,再逐步深入局部细节。

十二、每个阶段的验收标准

学习路线如果没有验收标准,很容易变成“我好像学过”。可以给每个阶段设置明确产出。

Linux 基础阶段:

  • 能通过串口或 SSH 登录设备。
  • 能查看进程、内存、磁盘、网络状态。
  • 能看懂 mountdf -hdmesg 的基本输出。
  • 能判断某个目录是只读、可写还是 tmpfs。
  • 能写简单 shell 脚本完成启动、日志清理、配置检查。

交叉编译阶段:

  • 能独立编译一个 C 程序并在板子上运行。
  • 能判断可执行文件的架构是否正确。
  • 能排查动态库缺失和动态链接器路径错误。
  • 能用 Makefile 或 CMake 管理多文件项目。
  • 能把应用打进 rootfs 或通过网络部署到设备。

RootFS 阶段:

  • 能说明 /etc/inittabrcS、mdev、BusyBox 的作用。
  • 能添加一个开机自启动服务。
  • 能区分系统分区、数据分区和临时分区。
  • 能把配置保存到可写分区,并在重启后恢复。
  • 能裁剪或添加常用调试工具。

系统服务阶段:

  • 能写一个常驻进程。
  • 能处理配置加载、日志输出、异常重启。
  • 能实现网络断线重连。
  • 能通过 MQTT 或 HTTP 接收配置指令。
  • 能保证异常断电后配置不损坏。

驱动和内核阶段:

  • 能看懂设备树中一个外设节点。
  • 能通过 dmesg 判断驱动是否 probe 成功。
  • 能通过 sysfs 或 dev 节点验证外设。
  • 能修改内核配置并重新编译内核。
  • 能定位简单的引脚复用、I2C 地址、GPIO 极性问题。

工程交付阶段:

  • 能一键构建固件。
  • 能记录构建版本、Git commit、构建时间。
  • 能导出日志和关键配置。
  • 能完成 OTA 升级和失败回滚。
  • 能写出基本的生产测试或冒烟测试脚本。

十三、三个循序渐进的练习项目

13.1 项目一:做一个最小可启动系统

目标:

  • 使用 Buildroot 生成 rootfs。
  • 能通过串口进入 shell。
  • 能自动挂载 /proc/sys/dev
  • 能通过网口或 USB 网络登录设备。

产出:

1
2
3
output/images/
board/<vendor>/<board>/rootfs_overlay/
README.md

验收:

1
2
3
4
uname -a
cat /proc/cpuinfo
mount
ifconfig

这个项目解决“系统怎么跑起来”的问题。

13.2 项目二:做一个设备配置服务

目标:

  • 写一个 device_agent 常驻进程。
  • 启动时读取 /data/config/device.json
  • 支持 MQTT 下发配置。
  • 配置变更后写入 /data/config
  • 重启后配置不丢失。

可以从几个配置项开始:

1
2
3
4
5
{
"device_name": "gateway-001",
"timezone": "Asia/Shanghai",
"log_level": "info"
}

验收:

  • MQTT 下发时区后,设备 ack 成功。
  • /data/config/device.json 被更新。
  • 重启设备后配置仍然存在。
  • 日志里能看到配置加载和变更记录。

这个项目会把 rootfs、可写分区、MQTT、配置持久化和服务重启串起来。

13.3 项目三:做一个外设采集服务

目标:

  • 通过 I2C、UART 或 GPIO 读取一个外设。
  • 把数据周期性上报到 MQTT。
  • 网络断开时本地缓存。
  • 网络恢复后补发。
  • 进程异常退出后自动重启。

验收:

  • 外设拔掉后服务不会崩溃。
  • 网络断开后日志有明确状态。
  • 网络恢复后数据能继续上报。
  • 长时间运行内存没有明显增长。

这个项目开始接近真实产品,会同时考察驱动、业务服务、异常处理和稳定性。

十四、常见误区

误区一:一开始就学驱动。

驱动很重要,但不是所有嵌入式 Linux 工作都从驱动开始。如果基础系统、rootfs、交叉编译、调试工具都不熟,驱动问题会很难定位。

误区二:只会在开发板上手工改文件。

手工改文件可以调试,但不能交付。真正的项目要能通过构建脚本复现所有修改。

误区三:忽略异常断电。

嵌入式设备经常会被直接断电。如果配置文件写入没有原子替换、没有 fsync、没有备份,迟早会遇到配置损坏。

误区四:日志只打印“成功”和“失败”。

现场排查需要上下文。日志至少要包含模块、操作、关键参数、错误码和时间。

误区五:把能跑当成完成。

能跑只是第一步。能长期运行、能自恢复、能升级、能定位问题,才接近可交付。

十五、一个 8 周入门计划

如果每天能投入一到两个小时,可以按 8 周节奏推进。这个计划不追求覆盖所有知识点,目标是尽快形成完整开发闭环。

第 1 周:熟悉开发板和 Linux 基础。

  • 接好串口。
  • 跑通官方镜像。
  • 学会查看启动日志。
  • 熟悉 pstopmountdfdmesg
  • 搞清楚哪些目录可写,哪些目录重启丢失。

验收结果:

1
能从断电开机开始,完整记录一次启动日志,并说明系统从哪里挂载 rootfs。

第 2 周:交叉编译和部署。

  • 安装交叉工具链。
  • 编译 hello world。
  • 编译一个带多文件的 C 程序。
  • 用 scp、tftp 或 adb 推到板子。
  • 解决动态库缺失问题。

验收结果:

1
能在开发机编译应用,并在目标板稳定运行。

第 3 周:RootFS 和启动脚本。

  • 理解 BusyBox init。
  • 修改 rootfs overlay。
  • 增加开机启动脚本。
  • 把日志写到 /data/log
  • 把配置写到 /data/config

验收结果:

1
设备重启后,自己的服务能自动启动,配置和日志不会丢失。

第 4 周:网络和 MQTT。

  • 配置网络。
  • 写一个 MQTT 客户端。
  • 支持订阅命令 topic。
  • 支持发布状态 topic。
  • 支持断线重连。

验收结果:

1
通过 MQTT 下发配置,设备能执行并返回 ack。

第 5 周:服务稳定性。

  • 增加日志级别。
  • 增加配置校验。
  • 增加异常退出重启。
  • 增加看门狗喂狗。
  • 增加配置原子写入。

验收结果:

1
杀掉业务进程后能自动恢复,异常断电后配置文件不损坏。

第 6 周:Buildroot 定制。

  • 保存 defconfig。
  • 加入自定义 package。
  • 加入 rootfs overlay。
  • 加入 tzdata、strace、tcpdump 等调试工具。
  • 生成完整镜像。

验收结果:

1
从干净代码仓库执行构建命令,可以生成可烧录镜像。

第 7 周:设备树和外设。

  • 找一个简单外设,例如 GPIO LED、I2C 温度传感器。
  • 看懂对应设备树节点。
  • 查看驱动 probe 日志。
  • 通过 sysfs、dev 节点或业务程序读取外设。

验收结果:

1
能说明外设从设备树到驱动再到用户态访问的完整路径。

第 8 周:打包、升级和排查。

  • 增加固件版本文件。
  • 增加日志导出脚本。
  • 做一次 OTA 或手动升级流程。
  • 模拟升级失败和回滚。
  • 整理常见问题排查文档。

验收结果:

1
拿到一台异常设备时,能导出日志、确认版本、判断配置、定位大致问题范围。

十六、开发机到目标板的日常工作流

实际开发中,要把“改代码、编译、部署、运行、看日志”做顺。

一个简单工作流:

1
2
3
4
5
6
7
开发机修改代码
-> 交叉编译
-> 推送到目标板 /tmp 或 /usr/bin
-> 重启服务
-> 查看日志
-> 验证功能
-> 固化到 Buildroot package 或 rootfs overlay

调试阶段可以直接推文件:

1
2
scp device_agent root@192.168.1.100:/tmp/device_agent
ssh root@192.168.1.100 'chmod +x /tmp/device_agent && /tmp/device_agent'

确认功能稳定后,再放进构建系统。不要长期依赖手工拷贝,否则换一台设备或重新烧录后,环境就不可复现。

如果设备上空间有限,可以把调试工具放到 NFS 或临时目录:

1
2
mount -t nfs 192.168.1.10:/nfs/root /mnt
/mnt/tools/strace -f /usr/bin/device_agent

这能减少对量产 rootfs 的污染。

十七、建议准备的工具清单

开发机侧:

  • 交叉编译工具链。
  • minicom、picocom 或其他串口工具。
  • tftp、nfs、ssh、scp。
  • gdb-multiarch。
  • CMake、Make、Ninja。
  • Git。
  • mqttx、mosquitto_pub、mosquitto_sub。
  • Wireshark。

目标板侧:

  • BusyBox。
  • iproute2 或基础网络命令。
  • strace。
  • tcpdump。
  • gdbserver。
  • hexdump。
  • i2c-tools。
  • ethtool。
  • logrotate 可选。

不是所有工具都要进量产镜像。可以分成两个版本:

1
2
debug image: 工具完整,方便定位问题
release image: 尽量精简,只保留必要能力

这样既能提高开发效率,也能控制最终包体和攻击面。

总结

嵌入式 Linux 入门不应该从某一个孤立知识点开始,而应该从完整系统视角出发。先理解启动链路,再掌握 rootfs 和交叉编译,然后开发系统服务,最后深入内核、设备树和驱动。

如果目标是能做项目,而不是只看教程,最重要的是尽早拿一块板子,把镜像构建、应用开发、配置持久化、日志排查、网络通信和异常恢复串起来。每个阶段都应该有明确产出和验收标准。真正的开发能力,就是在这条链路上不断补齐短板。

其他文章
目录导航 置顶
  1. 1. 系列目录
  2. 2. 一、先建立整体地图
  3. 3. 二、第一阶段:Linux 基础要够用
  4. 4. 三、第二阶段:C 语言和交叉编译
  5. 5. 四、第三阶段:把板子启动起来
  6. 6. 五、第四阶段:理解 RootFS
  7. 7. 六、第五阶段:用 Buildroot 或 Yocto 构建系统
  8. 8. 七、第六阶段:开发系统服务和业务应用
  9. 9. 八、第七阶段:再深入 Kernel 和驱动
  10. 10. 九、第八阶段:学会调试和定位问题
  11. 11. 十、第九阶段:形成工程闭环
  12. 12. 十一、一条推荐学习路线
  13. 13. 十二、每个阶段的验收标准
  14. 14. 十三、三个循序渐进的练习项目
    1. 14.1. 13.1 项目一:做一个最小可启动系统
    2. 14.2. 13.2 项目二:做一个设备配置服务
    3. 14.3. 13.3 项目三:做一个外设采集服务
  15. 15. 十四、常见误区
  16. 16. 十五、一个 8 周入门计划
  17. 17. 十六、开发机到目标板的日常工作流
  18. 18. 十七、建议准备的工具清单
  19. 19. 总结
请输入关键词进行搜索