学习嵌入式 Linux 最容易遇到的问题,不是资料太少,而是资料太散。今天看驱动,明天看 Buildroot,后天又去研究 U-Boot,结果每个概念都知道一点,但真正拿到一块板子时,还是不知道从哪里开始开发。
更合理的方式,是按真实项目的开发链路学习:先能把系统跑起来,再能改 rootfs,再能开发应用,最后再深入内核和驱动。
系列目录
这篇文章作为总览,后续每个方向拆成独立文章继续展开:
- 嵌入式 Linux 系列 01:开发环境、串口和第一块板子
- 嵌入式 Linux 系列 02:交叉编译、动态库和部署
- 嵌入式 Linux 系列 03:从上电到 Shell 的启动链路
- 嵌入式 Linux 系列 04:RootFS 结构、启动脚本和配置持久化
- 嵌入式 Linux 系列 05:Buildroot 构建、overlay 和自定义 package
- 嵌入式 Linux 系列 06:设备服务、MQTT 协议和配置持久化
- 嵌入式 Linux 系列 07:调试工具和问题定位方法
- 嵌入式 Linux 系列 08:设备树和驱动入门
- 嵌入式 Linux 系列 09:OTA 升级、双分区和失败回滚
- 嵌入式 Linux 系列 10:看门狗、进程守护和自恢复
- 嵌入式 Linux 系列 11:存储、Flash 寿命和异常断电保护
- 嵌入式 Linux 系列 12:网络配置、连通性和 MQTT 排查
- 嵌入式 Linux 系列 13:生产测试、出厂配置和版本追踪
- 嵌入式 Linux 系列 14:基础安全加固、账号和证书
- 嵌入式 Linux 系列 15:系统时间、RTC、NTP 和时区
- 嵌入式 Linux 系列 16:日志、观测和现场问题包
- 嵌入式 Linux 系列 17:进程模型、线程和 IPC 选择
- 嵌入式 Linux 系列 18:UART、I2C、SPI 和 GPIO 实战入口
- 嵌入式 Linux 系列 19:CPU、内存和启动性能调优
- 嵌入式 Linux 系列 20:功耗、休眠和电源稳定性
- 嵌入式 Linux 系列 21:分区规划、镜像布局和数据保留
- 嵌入式 Linux 系列 22:固件版本、配置版本和升级迁移
- 嵌入式 Linux 系列 23:远程诊断、日志导出和现场维护
- 嵌入式 Linux 系列 24:自动化测试、冒烟测试和硬件在环
- 嵌入式 Linux 系列 25:内核配置、模块和裁剪
- 嵌入式 Linux 系列 26:C 语言服务代码的工程实践
- 嵌入式 Linux 系列 27:U-Boot 环境变量和启动脚本
- 嵌入式 Linux 系列 28:设备节点、mdev/udev 和热插拔
- 嵌入式 Linux 系列 29:MTD、UBI、eMMC 和文件系统选择
- 嵌入式 Linux 系列 30:SDK、项目目录和团队协作
- 嵌入式 Linux 系列 31:C、C++、Go、Rust 的语言选型
- 嵌入式 Linux 系列 32:设备身份、证书烧录和密钥保护
一、先建立整体地图
一个典型嵌入式 Linux 系统可以拆成几层:
1 | 硬件 |
从开发角度看,对应的工作是:
- 能烧录镜像。
- 能看串口日志。
- 能进 shell。
- 能改启动脚本。
- 能交叉编译应用。
- 能打包 rootfs。
- 能配置内核和设备树。
- 能调试应用、服务、驱动和启动问题。
新手不要一开始就钻进驱动源码。驱动当然重要,但如果连系统启动流程、rootfs 结构、交叉编译和日志排查都不熟,直接看驱动会非常吃力。
二、第一阶段:Linux 基础要够用
嵌入式 Linux 开发离不开基础命令,但不需要先把所有命令背完。
优先掌握这些:
1 | ls |
在精简 rootfs 里可能没有完整 GNU 工具,很多命令来自 BusyBox。BusyBox 参数比桌面发行版少,脚本写法要保守一点。
必须理解几个目录:
1 | /bin 基础命令 |
如果目标系统只读,就要额外关注哪些目录挂载到了可写分区。例如配置可能放在 /data/config,日志可能放在 /data/log。
三、第二阶段:C 语言和交叉编译
嵌入式 Linux 上的底层应用大多仍然离不开 C/C++。
最低要求是:
- 能读懂指针、结构体、函数指针。
- 能处理文件 IO。
- 能处理 socket。
- 能写多线程程序。
- 能理解动态库和静态库。
- 能用 Makefile 或 CMake 构建项目。
交叉编译是入门时必须过的一关。主机通常是 x86_64,目标设备可能是 ARM、AArch64、MIPS 或 RISC-V。
一个最小示例:
1 |
|
交叉编译:
1 | arm-linux-gnueabihf-gcc hello.c -o hello |
拷到板子运行:
1 | scp hello root@192.168.1.100:/tmp/ |
如果运行时报:
1 | No such file or directory |
不一定是文件不存在,也可能是动态链接器路径不匹配。可以用:
1 | readelf -l hello | grep interpreter |
检查目标 rootfs 是否有对应的动态链接器。
四、第三阶段:把板子启动起来
开发板到手后,第一件事不是改代码,而是把启动链路跑通。
需要准备:
- 串口工具。
- 烧录工具。
- 官方 SDK 或镜像。
- 网线或 USB 网络。
- TFTP/NFS 可选。
- 原理图和芯片手册可选。
串口日志非常关键。启动失败时,它通常是唯一可靠的信息来源。
启动日志里重点看:
1 | U-Boot 版本 |
如果内核起来了但进不了 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 | mount |
一个可靠的嵌入式系统,通常会把系统镜像和用户数据分开:
1 | rootfs: 只读,随固件升级 |
六、第五阶段:用 Buildroot 或 Yocto 构建系统
手工拼 rootfs 可以帮助理解原理,但真实项目更推荐用构建系统。
Buildroot 适合:
- 系统相对简单。
- 想快速生成工具链、内核、rootfs。
- 团队希望配置简单。
- 产品包数量不算特别复杂。
Yocto 适合:
- 产品线复杂。
- 需要精细的软件包管理。
- 多平台、多版本长期维护。
- 对许可证、组件追踪要求高。
入门阶段建议先用 Buildroot。它的学习路径更短,能让你更快看到完整系统。
Buildroot 常见目录:
1 | configs/ 板级默认配置 |
常见操作:
1 | make menuconfig |
项目里不要只保存最终镜像,更要保存配置文件和构建脚本,否则后续版本很难复现。
七、第六阶段:开发系统服务和业务应用
大多数嵌入式 Linux 开发岗位,日常工作并不是天天写驱动,而是开发设备上的系统服务和业务应用。
常见任务包括:
- 网络配置。
- MQTT/HTTP 通信。
- OTA 升级。
- 日志采集。
- 设备参数管理。
- 本地数据库或配置文件管理。
- 外设控制。
- 看门狗喂狗。
- 异常自恢复。
服务开发要关注几个问题:
- 进程如何启动。
- 崩溃后是否自动重启。
- 配置变更如何生效。
- 日志是否可追踪。
- 网络断开后如何重连。
- 写 flash 是否有频率控制。
- 异常断电后配置是否损坏。
一个服务启动脚本示例:
1 |
|
这不是最终的完美方案,但能说明一个嵌入式服务最基本的诉求:能启动、能记录日志、异常退出后能回来。
八、第七阶段:再深入 Kernel 和驱动
当你能稳定构建系统、开发应用、修改 rootfs 后,再深入内核和驱动会顺很多。
驱动学习建议按外设类型推进:
- GPIO。
- UART。
- I2C。
- SPI。
- PWM。
- ADC。
- USB。
- Ethernet。
- Display。
同时要理解 Device Tree:
1 | &i2c1 { |
设备树不是驱动本身,而是描述硬件连接关系。驱动根据 compatible、reg、interrupts、clocks、gpios 等信息找到设备并完成初始化。
驱动调试常用工具:
1 | dmesg |
驱动问题不要只看代码。原理图、引脚复用、电源时序、时钟、复位脚、设备树配置都可能是根因。
九、第八阶段:学会调试和定位问题
嵌入式开发真正拉开差距的地方,是定位问题的能力。
常见问题和工具:
- 启动失败:串口日志、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 | /etc/os-release |
应用启动时打印版本:
1 | device_agent version: 1.4.2 |
现场设备出问题时,版本信息、日志和配置快照往往比代码本身更重要。
十一、一条推荐学习路线
可以按下面顺序推进:
- 熟悉 Linux 命令和目录结构。
- 学会 C 语言、Makefile、交叉编译。
- 跑通开发板官方镜像。
- 看懂 U-Boot、Kernel、RootFS 的启动日志。
- 用 Buildroot 生成一个最小系统。
- 修改 rootfs overlay,加入启动脚本和配置文件。
- 写一个能开机启动的业务服务。
- 加入 MQTT、日志、配置持久化、看门狗。
- 学会用 gdb、strace、tcpdump、dmesg 定位问题。
- 再深入设备树和常见外设驱动。
- 最后补齐 OTA、回滚、量产配置和自动化测试。
这条路线的核心是先跑通完整链路,再逐步深入局部细节。
十二、每个阶段的验收标准
学习路线如果没有验收标准,很容易变成“我好像学过”。可以给每个阶段设置明确产出。
Linux 基础阶段:
- 能通过串口或 SSH 登录设备。
- 能查看进程、内存、磁盘、网络状态。
- 能看懂
mount、df -h、dmesg的基本输出。 - 能判断某个目录是只读、可写还是 tmpfs。
- 能写简单 shell 脚本完成启动、日志清理、配置检查。
交叉编译阶段:
- 能独立编译一个 C 程序并在板子上运行。
- 能判断可执行文件的架构是否正确。
- 能排查动态库缺失和动态链接器路径错误。
- 能用 Makefile 或 CMake 管理多文件项目。
- 能把应用打进 rootfs 或通过网络部署到设备。
RootFS 阶段:
- 能说明
/etc/inittab、rcS、mdev、BusyBox 的作用。 - 能添加一个开机自启动服务。
- 能区分系统分区、数据分区和临时分区。
- 能把配置保存到可写分区,并在重启后恢复。
- 能裁剪或添加常用调试工具。
系统服务阶段:
- 能写一个常驻进程。
- 能处理配置加载、日志输出、异常重启。
- 能实现网络断线重连。
- 能通过 MQTT 或 HTTP 接收配置指令。
- 能保证异常断电后配置不损坏。
驱动和内核阶段:
- 能看懂设备树中一个外设节点。
- 能通过
dmesg判断驱动是否 probe 成功。 - 能通过 sysfs 或 dev 节点验证外设。
- 能修改内核配置并重新编译内核。
- 能定位简单的引脚复用、I2C 地址、GPIO 极性问题。
工程交付阶段:
- 能一键构建固件。
- 能记录构建版本、Git commit、构建时间。
- 能导出日志和关键配置。
- 能完成 OTA 升级和失败回滚。
- 能写出基本的生产测试或冒烟测试脚本。
十三、三个循序渐进的练习项目
13.1 项目一:做一个最小可启动系统
目标:
- 使用 Buildroot 生成 rootfs。
- 能通过串口进入 shell。
- 能自动挂载
/proc、/sys、/dev。 - 能通过网口或 USB 网络登录设备。
产出:
1 | output/images/ |
验收:
1 | uname -a |
这个项目解决“系统怎么跑起来”的问题。
13.2 项目二:做一个设备配置服务
目标:
- 写一个
device_agent常驻进程。 - 启动时读取
/data/config/device.json。 - 支持 MQTT 下发配置。
- 配置变更后写入
/data/config。 - 重启后配置不丢失。
可以从几个配置项开始:
1 | { |
验收:
- MQTT 下发时区后,设备 ack 成功。
/data/config/device.json被更新。- 重启设备后配置仍然存在。
- 日志里能看到配置加载和变更记录。
这个项目会把 rootfs、可写分区、MQTT、配置持久化和服务重启串起来。
13.3 项目三:做一个外设采集服务
目标:
- 通过 I2C、UART 或 GPIO 读取一个外设。
- 把数据周期性上报到 MQTT。
- 网络断开时本地缓存。
- 网络恢复后补发。
- 进程异常退出后自动重启。
验收:
- 外设拔掉后服务不会崩溃。
- 网络断开后日志有明确状态。
- 网络恢复后数据能继续上报。
- 长时间运行内存没有明显增长。
这个项目开始接近真实产品,会同时考察驱动、业务服务、异常处理和稳定性。
十四、常见误区
误区一:一开始就学驱动。
驱动很重要,但不是所有嵌入式 Linux 工作都从驱动开始。如果基础系统、rootfs、交叉编译、调试工具都不熟,驱动问题会很难定位。
误区二:只会在开发板上手工改文件。
手工改文件可以调试,但不能交付。真正的项目要能通过构建脚本复现所有修改。
误区三:忽略异常断电。
嵌入式设备经常会被直接断电。如果配置文件写入没有原子替换、没有 fsync、没有备份,迟早会遇到配置损坏。
误区四:日志只打印“成功”和“失败”。
现场排查需要上下文。日志至少要包含模块、操作、关键参数、错误码和时间。
误区五:把能跑当成完成。
能跑只是第一步。能长期运行、能自恢复、能升级、能定位问题,才接近可交付。
十五、一个 8 周入门计划
如果每天能投入一到两个小时,可以按 8 周节奏推进。这个计划不追求覆盖所有知识点,目标是尽快形成完整开发闭环。
第 1 周:熟悉开发板和 Linux 基础。
- 接好串口。
- 跑通官方镜像。
- 学会查看启动日志。
- 熟悉
ps、top、mount、df、dmesg。 - 搞清楚哪些目录可写,哪些目录重启丢失。
验收结果:
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 | 开发机修改代码 |
调试阶段可以直接推文件:
1 | scp device_agent root@192.168.1.100:/tmp/device_agent |
确认功能稳定后,再放进构建系统。不要长期依赖手工拷贝,否则换一台设备或重新烧录后,环境就不可复现。
如果设备上空间有限,可以把调试工具放到 NFS 或临时目录:
1 | mount -t nfs 192.168.1.10:/nfs/root /mnt |
这能减少对量产 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 | debug image: 工具完整,方便定位问题 |
这样既能提高开发效率,也能控制最终包体和攻击面。
总结
嵌入式 Linux 入门不应该从某一个孤立知识点开始,而应该从完整系统视角出发。先理解启动链路,再掌握 rootfs 和交叉编译,然后开发系统服务,最后深入内核、设备树和驱动。
如果目标是能做项目,而不是只看教程,最重要的是尽早拿一块板子,把镜像构建、应用开发、配置持久化、日志排查、网络通信和异常恢复串起来。每个阶段都应该有明确产出和验收标准。真正的开发能力,就是在这条链路上不断补齐短板。
- 本文链接: https://blog.hansong.icu/2026/06/26/Embedded_Linux_From_Entry_To_Development/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。