Skip to content

一切的源头:如何开始本项目的学习 ​

本篇是 start 卷的卷首篇,也是整个教程的入口。笔者本来只想画一张路线图,收到反馈才发现:对嵌入式 Linux 不熟的朋友,看路线图照样一头雾水。所以本篇把入门前该弄明白的事一次讲清楚——它是什么、和单片机差在哪、这个项目怎么分卷、您该从哪条路进。下一篇工具链安装才是真正动手的第一步,这里先把地图摊开给咱们自己看。

前置知识 · 环境

  • 零基础或命令行不熟的朋友,您先去 Linux 基础预备营(35 章 Ubuntu 实用教程,专为嵌入式准备)补课,补完再回来
  • 本篇是导览章,不跑命令;后面各章实操的前提是 Ubuntu 22.04+ 或 WSL2、有 sudo 权限、会基本的命令行操作
  • 路径上下文:本篇在 document/tutorial/start/ 卷,讲的是 ~/imx-forge 这棵源码树;全项目用 Arm GNU Toolchain 15.2.Rel1 交叉工具链(装在 /opt/arm-gnu-toolchain)构建

嵌入式 Linux 并不容易。绝大多数情况下,咱们倾向于在 Linux 开发环境(专业点说,是 Linux 的各个发行版,比如 Ubuntu、Debian,或者笔者更为中意的 Arch Linux)里开发嵌入式开发板的功能。

从最本质上讲,咱们做的嵌入式 Linux 同单片机开发有区别,但又没有区别。没有区别的地方在哪里呢?

从驱动开发的角度聊,两者都面向硬件:了解硬件的基础特性之后,用软件代码去控制它。比如咱们用 C 语言开发驱动,这是因为 Linux 提供的接口抽象只有 C,近年来才慢慢有了 Rust。从宏观上讲,单片机开发最本质的东西没有变:用 C 控制您的硬件。

从应用开发的角度聊,还是面向业务。您把手头的需求搞清楚,比匆忙行动更重要,这是软件工程的要义。只不过用的是 C++、Rust,或者其他业务需要的语言,配合特定的框架,就像您写 OLED 程序可能会用 u8g2 库、驱动 MPU6050 会拿别人封装好的库来用,一个道理。

抓住这些根,咱们要聊的区别就只剩技术路线不同。这正是下面要展开的:不一样的地方。

嵌入式 Linux 到底是个什么东西? ​

很多朋友第一次接触“嵌入式 Linux”这个词的时候,脑子里是一团浆糊。您可能玩过 Arduino、玩过 STM32,觉得“嵌入式”就是那点东西:初始化时钟、配置 GPIO、写几个中断,然后在 while(1) 里跑业务逻辑。这没错,这是经典的单片机开发。再复杂一些,也就是上个 RTOS,让一个简单的调度器(跟 Linux 的调度器一比,真的简单太多了)托管多任务切换的细节。

但嵌入式 Linux 不一样。嵌入式 Linux 等于完整的 Linux 操作系统,加上跑在特定硬件平台上的应用系统。它不是什么“超级单片机”,它就是一台真正的计算机——咱们要做的,是对一台复杂度和您桌上电脑差不多的东西做软硬件编程!

您平时在电脑上用的 Ubuntu、Debian、Arch Linux,本质上和嵌入式 Linux 跑的东西是一样的。只不过您的电脑用 x86-64 处理器,嵌入式设备用 ARM、RISC-V 这类处理器。

这下麻烦了。经典的单片机开发里,咱们几乎没有硬件和软件的隔离。

没有隔离的时候,咱们叫裸机开发。以 ARM 芯片为例(RISC-V 笔者只在学 XV6 玩具操作系统、与人交流 CPU 架构以及短暂学计算机体系结构的时候接触过,这里不下结论),您的所有逻辑都被前后台架构托管:任务串行执行,基于状态机跳转任务流,顶多中断打断一下、改个状态。这样的架构,复杂度可以说是入门级别的。

即便有 RTOS 隔离,也只是被很轻的一层抽象挡了一下,咱们离硬件还是很近。咱们的代码对单片机而言是一视同仁的,有时候眼睛一闭,您可能都能想到现在这颗 ARM 芯片上跑的是什么指令。

到了嵌入式 Linux 就变了。CPU 主频飙到几百 MHz 甚至 1GHz,内存到了几百 MB 甚至几 GB,片上硬件的复杂度远远超出您熟悉的开发板。这就要求一个更复杂的操作系统来管,您知道的,就是 Linux。

MMU 与虚拟内存一上场,现代操作系统就和您熟悉的 RTOS 拉开了根本区别。页表负责把虚拟地址翻译成物理地址,每个进程都有自己的一套地址空间,互相之间看不见、也动不了,这就是进程隔离。内存管理也跟着升级。整页的分配交给 Buddy System 这样的页级内存管理;比页更小的对象,再由 Slab/SLUB 这类对象缓存分配器接手。咱们在单片机或轻量 RTOS 上用的那个简单堆分配器,到这儿就不够看了。

任务调度这边变化同样彻底:咱们熟悉的裸机循环或简单优先级抢占,到 Linux 里变成以公平调度类为核心的调度体系,老内核是 CFS,6.6 起演进为 EEVDF。实时任务则另有 SCHED_FIFO、SCHED_RR 这些实时调度策略兜底。

文件系统这边的道理一样。简单文件系统一些朋友可能玩过,但对嵌入式 Linux 而言,只有 FAT32 已经满足不了您的系统需求:EXT4、Btrfs 这类现代文件系统带来日志恢复、权限管理、大容量支持、延迟分配与写时复制。同时进来的还有网络设备与 TCP/IP 协议栈,都长在内核里。

最关键的一道分界是用户态与内核态隔离:应用程序没法直接访问硬件资源,必须通过系统调用让内核统一完成设备管理、资源调度与安全控制。这种分层架构也让驱动、进程管理、文件系统与网络协议栈各自模块化。这些东西在裸机或轻量 RTOS 的单片机环境里要么没有,要么是您自己造轮子拼出来的简陋版本,系统复杂度、抽象层级与工程规模都和现代通用操作系统有本质差异。

当然,这些只是景观。您要是不像笔者这样喜欢看 Linux 内核,那无所谓,知道就够了,实际上您极少改动这里的代码。

所以一些朋友跟笔者吐槽嵌入式 Linux“不自在”,原因其实很简单:您是在一个已有的、成熟的操作系统框架下,去适配硬件、开发应用。这和单片机那种“一切从零开始”的思路完全不同。

和单片机开发到底有啥区别? ​

这个问题笔者当年也纠结过好久,踩了一堆坑之后,总结出这么几个关键区别。

启动链条的差异 ​

单片机的启动简单粗暴:

text
上电复位 → 硬件与 C 运行环境初始化(汇编启动文件) → 跳到 main() → 进入 while(1) 死循环,开干

嵌入式 Linux 这边,咱们要面对的是一条完整的启动链条:

text
上电 → 厂商固化在 ROM 里的代码 → Bootloader(U-Boot) → Linux 内核 → RootFS 挂载 → Init 进程 → 您的应用

看到区别了吗?单片机上电之后您的代码就开始跑了;嵌入式 Linux 的每一个环节都可能出问题,每一个环节您都得懂。咱们把最容易撞的三种情况列成一张表:

现象根因解法
串口一点消息都没有先查串口侧:TX/RX 接线交叉、波特率、设备名;排除后再查启动侧:拨码启动介质、U-Boot 烧写偏移排查顺序见 串口工具使用 的常见问题表;启动侧回 U-Boot 教程
内核启动中途 Kernel Panic设备树写错,内核眼里的硬件和实际对不上回 内核教程 核对设备树
Panic 卡在挂载根文件系统RootFS 路径、格式或网络参数不对,init 起不来回 RootFS 教程 查挂载参数

这三种笔者全撞过,RootFS 那个挂载报错见得太多次、都快背出来了——所以 U-Boot、内核、RootFS 各有一卷教程,都是绕不过的坎。

开发模式的差异 ​

单片机的开发模式里,一个工程驱动和应用挤在同一个 main.c,直接操作寄存器,全局变量随便用,中断里想干啥干啥。撑死了再封装一下,笔者是真没见过比“丢一个函数指针和参数就能跑起一个应用”更简单的应用派发代码了。

嵌入式 Linux 不一样:驱动和应用完全分离,一个在内核空间,一个在用户空间。您不能直接操作硬件,得走系统调用。内核编程还有一堆限制:不能用传统的 C 标准库,不能写同步的无限循环——内核态死循环会把 CPU 占死,整块板子失去响应,还得考虑并发安全。应用开发倒是和在 PC 上写程序差不多。这个分离是很多从单片机转过来的朋友最不适应的点,笔者的私信全是问这个的。

您不能在应用里直接写 GPIO->ODR = 0x01,得写一个驱动,让应用通过 /dev/xxx 设备文件和驱动交互。听着麻烦?确实麻烦。但这种麻烦换来的是安全性、稳定性和可维护性。

调试方式的差异 ​

单片机这边 J-Link/SWD 一连,单点调试走起;想看东西了 printf 随便打,想在哪打在哪打;看寄存器、看内存、看变量,尽在掌握。嵌入式 Linux 的驱动调试主要靠 pr_* 家族和 dmesg,JTAG 或 kgdb 也能用,但笔者试过,内核态那条路的麻烦程度确实不低。用户态的应用调试曾经让很多朋友头疼,如今调试卷已经把它整理妥当:gdbserver 远程调试全链 把交叉调试从 Buildroot 开关到断点命中的整条链走通了,串口日志怎么读、panic 从哪下手,串口日志阅读路线 有专篇。您想看内存?拿到的只有虚拟地址,背后真正的物理地址早被 MMU 和页表管理子系统接管了。日志、/proc、sysfs,信息散落各处,很多时候您得靠推理和经验定位问题。很多朋友第一次在内核里用 pr_* 打了一堆日志,然后发现不知道去哪看,心态当场就崩了。

工具链的差异 ​

单片机这边其实挺省心,厂商 IDE 一键搞定:Keil、IAR、CubeIDE,图形界面点一点就完事。嵌入式 Linux 不一样,交叉编译工具链得自己装,Makefile 和 Kconfig 得自己写,很多时候您要和命令行打交道。这也是为什么咱们的教程从工具链开始:这是绕不过的第一步,连编译器都没有,后面的一切都无从谈起。

这个项目的层级结构 ​

好,说完了嵌入式 Linux 是什么、和单片机有什么区别,咱们来聊聊这个项目的结构。笔者尽量按“从底层到上层”的逻辑组织,您可以把它想象成一栋楼:先打地基,再一层一层往上盖。这栋楼只是入门主线的骨架,不是教程的全部:整个教程一共有 17 卷,剩下的烧录、Docker、实战那些,本节末尾一并点名。

最底层是工具链,这是地基。没有工具链您啥也干不了:U-Boot 编不了、内核编不了、应用编不了。教程从这里开始,您去看看工具链安装就知道了,这一步绕不过。

再往上是 U-Boot,您可以把它理解为嵌入式 Linux 的“第一个程序”。上电之后它先跑起来,初始化内存、串口、网络、存储这些硬件,然后把 Linux 内核加载进来,交出控制权。这块内容在 U-Boot 教程里。

然后是 Linux 内核,整个系统的核心。进程调度、内存管理、文件系统、设备驱动框架、网络协议栈,全是它在管。这块水挺深,内核教程 从编译配置到设备树一步步带您走。

内核启动后要挂载一个根文件系统,里面有 init 进程(第一个用户进程)、/bin 和 /sbin 下的工具、配置文件、库文件等等。咱们手搓这套的入门路径在 RootFS 教程:BusyBox 怎么编、inittab 怎么写、NFS 怎么挂,都有;项目现行的 rootfs 方案已经迁到 Buildroot 专栏(12 章),两条路都值得认识。

再往上是驱动开发,连接硬件和应用的桥梁。驱动负责硬件初始化、寄存器操作、向上提供统一接口、处理中断。这块是重头戏,字符设备基础 带您从内核空间与模块基础一路走到新旧 API 的字符设备实现,硬件访问(内存映射 I/O、硬件抽象层)各有专节;设备树在隔壁 设备树基础,并发机制则要回 内核教程 的内核并发机制部分。

盖楼之外还有几个横切的卷,哪一层都用得上。调试与排错(3 章)管用户态的 gdbserver 远程调试、strace 和串口日志怎么读,外加一套排障方法论。开发环境配置(6 篇)管 WSL2 环境、VSCode 远程开发、串口终端、clangd 交叉索引、主机和板子之间怎么传文件。把您的开发台搭舒服,是所有章节的前置投资。QEMU 板级模拟(8 章)把整块开发板搬进模拟器,手边没有实体板子也照样学。

最顶层就是您的应用了:通过设备文件访问驱动,调用各种系统库,做您想做的事。应用开发和在 PC 上写程序差不多,只是编译时要用交叉编译工具链。

楼外还有一圈按需取用的卷:镜像与烧录(10 章)负责把构建产物变成能烧的镜像、烧进 SD 卡和 eMMC,Docker 教程(2 章)用容器把构建环境一步搭好,实战演练和应用项目是整合练习,命令速查、构建进阶、第三方支持则是查资料、翻手册时才用得上的。入门主线走顺之前,咱们知道它们在哪就行。

如何开始学习? ​

这个问题没有标准答案,取决于您的背景。笔者给几条不同的路线,您可以对号入座。

如果您是完全的新手,连 Linux 命令行都不太熟,建议先去 Linux 基础预备营 补课,补完再回来。接下来按顺序来:搞懂工具链,这一步绕不过;然后了解 U-Boot,不用深入,知道它是干嘛的就行;接着在概念层面理解内核;再搞一下 RootFS,知道根文件系统是什么、里面有什么;最后开始驱动入门,那里才是真正开始写代码的地方。这条路径比较稳,慢一点,但基础打得牢。

如果您有单片机经验,可以跳过一些基础概念,重点盯那些不一样的点。工具链安装还是得看,它和单片机那套不一样;内核空间基础得补,这个概念单片机里没有但很重要;设备树也得学,单片机的硬件配置在代码里,Linux 的在设备树里。之后您就可以直接从字符设备基础开始了。

如果您想快速上手、尽快看到效果,可以走“实战路线”。不喜欢折腾工具链安装的,先把 Docker 环境搭起来(Docker 卷 有 2 章专门讲它),看 QUICK_START 快速开始就行,然后直接运行 ./scripts/release-all.sh 构建整套系统,把镜像烧到板子上(烧录专题在镜像与烧录卷,一共 10 章),先跑起来再说。跑起来之后您再回头慢慢研究各个模块,心里就有底了。

未实测标注

烧录到实际板子并上电验证这一步,要在 i.MX6ULL 实体开发板上完成;笔者写作的这个环境没有接板子,串口输出验证不了。具体烧录命令、镜像路径与参数,以 镜像与烧录 卷(09 SD 卡烧录、10 UUU/UMS eMMC 烧录)里实测过的流程为准,镜像路径以 out/release-latest/images/ 为准;QUICK_START 只作快速总览。

手边没有实际板子的朋友也别被挡在门外:QEMU 板级模拟(8 章)把整块开发板搬进了模拟器,跑咱们自己的内核和 rootfs,一样能学。

学习路线图(时间线) ​

最后笔者给一个大致的时间线参考。每个人情况不同,这只是笔者的估算。完整走一遍大概需要 3-4 周:Docker 环境搭建和工具链安装一天就够了(不想用 Docker 也行,WSL 或虚拟机都可以,效果一样);U-Boot 基础两三天;Linux 内核基础三五天;RootFS 构建两三天;驱动开发基础得一两周;最后的系统整合与调试是持续性的工作。这期间穿插在全程的还有环境配置(编辑器、串口、传文件的坑都在开发环境配置)和排错能力(出了问题怎么读日志、怎么定位,调试与排错 教的是方法),它们不值得单独排期,但会救您无数次。想快速上手的话,一周足够跑起来一个基本系统。

最后的一些唠叨 ​

嵌入式 Linux 的学习曲线确实比单片机陡。不过依笔者看,陡是因为要关注的东西多:工具链、U-Boot、内核、设备树、RootFS、驱动、应用,每个环节都是不小的一块,而单片机开发您只需要盯住板子和代码。好消息是,一旦您掌握了这条完整的链,就拥有了从零构建一个嵌入式 Linux 系统的能力:不受厂商 SDK 的束缚,想用哪个版本的内核就用哪个版本,想怎么定制系统就怎么定制,真正理解“底层”是什么。这种能力的价值,笔者认为远大于学习过程中的那些麻烦。这也是笔者开始这个项目的原因:它是一个用无数个串口终端的日夜堆出来的工程,目标就一个——让咱们更方便地定制自己的 i.MX6ULL 系统。

接下来去哪里? ​

如果您已经看到这里,说明您是真的想学。那么下一步:没有 Linux 基础的,先去 Linux 基础预备营;完全新手去工具链安装;想先看效果的去看 QUICK_START 快速开始;想直接写驱动的去字符设备基础;想先把开发台搭好(WSL2、编辑器、串口)的去开发环境配置;手边没板子的去QEMU 板级模拟;想学怎么定位问题的去调试与排错。

祝您在嵌入式 Linux 的世界里玩得开心!有问题随时去项目的 GitHub Issues 找咱们!

继续学习 ​

v1.0.4-37-gc79a127 · c79a127 · 2026-09-29