Skip to content

Commit 0191c15

Browse files
Better Roadmap for the imx-forge (#34)
feat: add roadmap for newbees ahead for kernel
1 parent d80035d commit 0191c15

7 files changed

Lines changed: 159 additions & 15 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ site/.vitepress/.build-tmp/
3131
.tmp_versions/
3232
modules.builtin
3333
modules.builtin.modinfo
34+
35+
.venv/
36+
37+
pnpm-workspace.yaml
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# 一切的源头——如何开始本项目的学习呢?
2+
3+
我本来打算直接画一个Roadmap的,但是我收到反馈——就算画Roadmap,那些不太了解嵌入式Linux的朋友仍然会一头雾水。笔者决定,必须要把常见的问题汇总一下,整理成路线清单。聊聊我们的嵌入式Linux开发到底是什么。只有这样,学习这个项目,您才知道我们到底从那个地方开始!
4+
5+
我需要首先说明的是——**嵌入式Linux**并不容易。绝大多数情况下,我们倾向于在**Linux开发环境**(专业的说,应当叫Linux的各个发行版,比如说UBuntu, Debian, 或者是笔者更为中意的Arch Linux等)开发嵌入式开发板的功能。
6+
7+
嵌入式Linux从最本质上讲,同嵌入式单片机开发有部分区别,但是又没有区别。没有区别的地方在哪里呢?
8+
9+
1. 从驱动开发的角度上聊,都是面向硬件,在了解硬件基础特性后,使用软件代码——比如说,我们会使用C语言开发驱动(这是因为Linux提供的接口抽象只有C,近年来才慢慢有Rust)。从宏观的角度上讲,单片机的最本质的东西没有变:使用C来控制您的硬件。
10+
11+
2. 从应用开发的角度上聊,还是面向业务。你把手头的需求搞清楚了,比匆忙行动更加重要。这是软件工程的要义。只不过用的是C++,或者是Rust,或者是其他您手头需要的业务语言,使用特定的框架,就像您开发OLED程序可能会去用u8g2库,驱动MPU6050用其他人写的封装好的库,等等!
12+
13+
抓住这些根,其实区别只有技术路线不同而已。这就是我们下面聊的,不一样的地方。
14+
15+
## 嵌入式Linux到底是个什么东西?
16+
17+
很多人第一次接触"**嵌入式Linux**"这个词的时候,脑子里是一团浆糊。你可能玩过Arduino,玩过STM32,觉得"**嵌入式**"就是那点东西:初始化时钟、配置GPIO、写几个中断、然后在while(1)里跑你的业务逻辑。这没错,这是经典**单片机开发**。再复杂一些,也就是上个**RTOS**,让简单的Schedular(跟Linux的比真的简单到爆炸了)托管您的多任务切换细节。
18+
19+
**嵌入式Linux**不一样。嵌入式Linux = 完整的**Linux操作系统** + 跑在特定硬件平台上的应用系统。它不是一个简单的"超级单片机",它就是一台真正的计算机,对!你对一个复杂度就是你的电脑差不多的东西,对他做软硬件编程!
20+
21+
你平时在电脑上用的Ubuntu、Debian、Arch Linux,本质上和嵌入式Linux跑的东西是一样的。只不过你的电脑用的是**x86-64**处理器,而嵌入式设备用的是**ARM****RISC-V**这类处理器。
22+
23+
这下麻烦了。经典的单片机开发中,我们几乎没有硬件和软件的隔离。
24+
25+
没有隔离的时候,我们叫**裸机开发**。以ARM芯片为例子(RISC-V笔者只有在学习玩具操作系统XV6的时候和跟人交流CPU架构,和短暂的学习计算机架构的时候短暂的接触过,所以不下结论),您的所有逻辑,都是被前后台架构托管。任务同步的串行执行,基于状态机的跳转任务流,顶多中断打断,修改状态。可以说这样的架构本身的复杂度可以说是入门级别的。
26+
27+
即便有RTOS隔离,也只是被RTOS很轻的一层抽象层抽象了一下,我们还是离硬件很近。我们的代码对于单片机而言是一视同仁的。有时候眼睛一闭上,您可能都能想得到现在您的ARM芯片上跑的什么指令。
28+
29+
**嵌入式Linux**变了。CPU的性能突然飙到了几百甚至1GMhz,主板硬件的内存芯片达到了几百甚至几个G的大小,片上的硬件的复杂度远远超出了您熟悉的开发板。这就要求我们使用更加复杂的操作系统控制——你知道的,就是**Linux**
30+
31+
随着 **MMU****虚拟内存**机制的引入,现代的操作系统根本区别于您熟悉的RTOS,它基于**页表**完成虚拟地址到物理地址的映射,并实现进程间地址空间隔离。内存管理也不再停留于单片机或轻量 RTOS 中常见的简单堆分配器,而是发展出了以 **Buddy System** 为核心的页级内存管理,并结合 **Slab/SLUB** 等对象缓存分配器提升内核分配效率与缓存局部性。
32+
33+
任务调度方面,系统也从裸机循环或简单优先级抢占模型,逐渐演化为 Linux 中基于 **CFS**(Completely Fair Scheduler)的通用公平调度体系,并同时保留 **SCHED_FIFO****SCHED_RR** 等实时调度能力,以兼顾吞吐、公平性与实时响应需求。
34+
35+
简单文件系统,我相信一些朋友可能玩过,对于嵌入式Linux而言,只有FAT32已经无法满足现代系统需求,**EXT4****Btrfs** 等现代文件系统开始提供日志恢复、权限管理、大容量支持、延迟分配以及 Copy-on-Write 等能力。与此同时,网络设备与 **TCP/IP 协议栈**的引入,也使操作系统逐渐具备完整的网络通信能力。
36+
37+
更重要的是,现代操作系统通过**用户态与内核态隔离**建立了严格的权限边界:应用程序无法直接访问硬件资源,而必须通过**系统调用**由内核统一完成设备管理、资源调度与安全控制。这种分层架构也使驱动、进程管理、文件系统与网络协议栈逐渐形成了高度模块化的系统设计。而在裸机或轻量 RTOS 的单片机环境中,这些东西在裸机单片机里要么没有,要么是你自己造轮子造出来的简陋版本,因此系统复杂度、抽象层级以及工程规模都与现代通用操作系统存在本质差异。
38+
39+
当然,这些只是景观。您要是不跟我一样喜欢看Linux内核,那无所谓,知道就得了,实际上你极少改这里的代码。
40+
41+
所以嵌入式Linux我注意到一些朋友跟我吐槽说不自在,其实很简单。你**是在一个已有的、成熟的操作系统框架下,去适配你的硬件,然后开发你的应用。这和单片机开发那种"一切从零开始"的思路完全不同。**
42+
43+
## 和单片机开发到底有啥区别?
44+
45+
这个问题我当年也纠结过好久。后来踩了一堆坑之后,我总结出这么几个关键区别:
46+
47+
### 1. 启动链条的差异
48+
49+
**单片机**
50+
```text
51+
上电 → 跳到main() → 说啥了干活呗
52+
```
53+
54+
**嵌入式Linux**
55+
```text
56+
上电 → 厂商小私货的ROM代码 → Bootloader(U-Boot) → Linux内核 → RootFS挂载 → Init进程 → 你的应用
57+
```
58+
59+
看到区别了吗?单片机的启动过程简单粗暴,上电之后你的代码就开始跑了。但嵌入式Linux要经过一个完整的**启动链条**。每一个环节都可能出问题,每一个环节你都得懂。
60+
61+
**U-Boot**配置错了?不好意思起不来。可能串口都没消息!
62+
**内核设备树**写错了?送你一个**Kernel Panic**,再见!
63+
**Rootfs**挂载失败?还是Kernel Panic,那个挂载错误我都快背出来了。。。见太多次了!
64+
65+
这就是为什么这个项目里有这么多关于**U-Boot****内核****Rootfs**的教程——这些东西是绕不过的坎。
66+
67+
### 2. 开发模式的差异
68+
69+
单片机开发模式下,一个工程里驱动和应用都在一个main.c里,直接操作寄存器,全局变量随便用,中断里想干啥干啥。撑死了再封装一下下,真的就是一下下了,我是没见过比丢一个函数指针和参数就能起飞一个"App"抽象更简单的应用派发代码了。
70+
71+
但嵌入式Linux不一样,**驱动和应用完全分离**,一个在**内核空间**,一个在**用户空间**。你不能直接操作硬件,得通过**系统调用**。内核编程有一堆限制——不能用任何传统的C标准库,不能同步的无限循环(小心被骂死哦),要考虑**并发安全**。应用开发和在PC上写程序差不多,这个分离是很多从单片机转过来的朋友最不适应的,我的私信全是问这个的,哈哈!
72+
73+
你不能在应用里直接 `GPIO->ODR = 0x01` 这么写。你得写个驱动,然后应用通过 `/dev/xxx` **设备文件**去和驱动交互。
74+
听起来很麻烦?确实麻烦。但这种麻烦带来的是安全性、稳定性和可维护性。
75+
76+
### 3. 调试方式的差异
77+
78+
单片机这块**J-Link/SWD**一连,单点调试走起,想看东西了?printf随便打,想在哪打在哪打!看寄存器、看内存、看变量,一切尽在掌握。但嵌入式Linux不太一样,驱动调试主要靠 `pr_*`**`dmesg`**,当然也能JTag或者kgdb,但是笔者试过,麻烦程度有点小爆炸。应用调试可以用**gdb**,但跨平台调试有点折腾。你想看内存?不好意思,你只能拿到**虚拟内存地址**,背后真正的物理地址早就被**MMU**硬件和页表管理子系统托管的看不到了。日志、**/proc****sysfs**,信息分散在各处,很多时候你得靠"推理"和"经验"去定位问题。很多朋友第一次在内核里 `pr_*` 打了一堆日志,然后发现不知道怎么看,心态当场崩了。
79+
80+
### 4. 工具链的差异
81+
82+
单片机这块其实挺省心的,厂商IDE一键搞定,**Keil****IAR****CubeIDE**什么的,图形界面点一点就完事了。但嵌入式Linux不一样,**交叉编译工具链**得自己装,**Makefile****Kconfig**得自己写,很多时候你要和命令行打交道。这也是为什么我们的教程从**工具链**开始——这是绕不过的第一步,你连编译器都没有,后面的一切都无从谈起。
83+
84+
## 这个项目的层级结构
85+
86+
好,说完了嵌入式Linux是什么,以及它和单片机的区别,我们来聊聊这个项目的结构。我尽量按照"从底层到上层"的逻辑来组织这个项目,你把它想象成一栋楼,得先打地基,然后一层一层往上盖。
87+
88+
最底层是**工具链**,这是地基。没有工具链,你啥也干不了,**U-Boot**编不了、**内核**编不了、应用编不了。我们的教程从这里开始,你去看看[工具链安装](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/start/01_start_from_toolchain)就知道了,这一步绕不过。
89+
90+
再往上是**U-Boot**,你可以把它理解为嵌入式Linux的"第一个程序"。上电之后它先跑起来,负责初始化硬件——内存、串口、网络、存储这些,然后把**Linux内核**加载进来,最后把控制权交出去。这块的内容在[U-Boot教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/uboot/)里,你想了解**Bootloader**的话可以看看。
91+
92+
然后是**Linux内核**,这是整个系统的核心。进程调度、内存管理、文件系统、设备驱动框架、网络协议栈,全是它在管。内核这块水挺深的,我们准备了[内核教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/kernel/),从编译配置到**设备树**,一步步带你走。
93+
94+
内核启动后需要挂载一个**根文件系统**,这里面有**init进程**(第一个用户进程)、/bin和/sbin这些目录下的工具、配置文件、库文件等等。**RootFS**这块的内容在[Rootfs教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/rootfs/)里,**BusyBox**怎么编、**inittab**怎么写、**NFS**怎么挂,都有。
95+
96+
再往上是**驱动开发**,这是连接硬件和应用的桥梁。驱动负责硬件初始化、寄存器操作、向上提供统一的接口、处理中断。这块是重头戏,[驱动教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/driver/)**字符设备**到并发机制,从硬件访问到设备树,内容相当扎实。
97+
98+
最顶层就是你的**应用**了,通过设备文件访问驱动,调用各种系统库,做你想做的事情。应用开发和在PC上写程序差不多,只是编译时要用**交叉编译工具链**
99+
100+
## 如何开始学习?
101+
102+
这个问题其实没有标准答案,取决于你的背景。我给几个不同的路线,你可以对号入座。
103+
104+
如果你是完全的新手,建议按顺序来。先搞懂**工具链**,这一步绕不过,必须先搞定。然后了解一下**U-Boot**,不需要深入,知道它是干嘛的就行。接着了解一下**内核**,同样概念层面理解即可。搞一下**RootFS**,知道根文件系统是啥、里面有什么。最后开始**驱动入门**,这里是真正开始写代码的地方。这个路径比较稳,虽然慢一点,但基础打得牢。
105+
106+
如果你有单片机经验,你可以跳过一些基础概念,重点关注那些不一样的点。**工具链安装**还是得看,和单片机不一样。然后重点看**内核空间基础**,这个概念在单片机里没有,但是很重要。**设备树**也得学,单片机里硬件配置在代码里,Linux里在设备树里。之后就可以直接从[字符设备驱动教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/driver/00_chardev_base/)开始了。
107+
108+
如果你想快速上手,想尽快看到效果,可以走"实战路线"。不喜欢折腾工具链安装的,先把**Docker环境**搭起来,看[QUICK_START.md](https://awesome-embedded-learning-studio.github.io/imx-forge/QUICK_START)就行。然后直接运行 `./scripts/release-all.sh` 构建整套系统,把镜像烧到板子上,先跑起来再说。跑起来之后,你再回头慢慢研究各个模块,这样心里有底。
109+
110+
## 学习路线图(时间线)
111+
112+
最后我给一个大致的时间线参考,当然每个人情况不同,这只是我的估算。如果你想完整走一遍,大概需要**3-4周**的时间。**Docker环境**搭建和**工具链安装**一天就够了(当然可以不Docker, 你是**WSL**好还是虚拟机好,都可以,一样的。),**U-Boot基础**两三天,**Linux内核基础**三五天,**RootFS构建**两三天,**驱动开发基础**得一两周,最后是**系统整合与调试**,这块是持续的工作。如果你想快速上手,**一周**时间足够让你跑起来一个基本系统。
113+
114+
## 最后的一些唠叨
115+
116+
说实话,**嵌入式Linux**的学习曲线确实比单片机要陡峭。但这并不是因为"更难",而是因为"东西更多"。单片机开发你只需要关注你的板子和你的代码,嵌入式Linux开发你需要关注**工具链****U-Boot****内核****设备树****RootFS****驱动****应用**,每一个环节都是一个不小的topic。但好消息是,一旦你掌握了这个完整的链条,你就拥有了从零构建一个嵌入式Linux系统的能力。这意味着你不受厂商SDK的束缚,想用哪个版本的内核就用哪个版本,想怎么定制系统就怎么定制系统,真正理解"底层"是什么。这种能力的价值,我觉得远大于学习过程中的那些麻烦。实际上,这也是我开始这个项目的原因—— **用 🔥 和无数串口终端堆出来的工程。希望我们可以更方便地自定义自己的 i.MX6ULL 系统。**
117+
118+
## 接下来去哪里?
119+
120+
如果你已经看到这里,说明你是真的想学。那么下一步,
121+
122+
完全新手去[工具链安装](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/start/01_start_from_toolchain)
123+
124+
想先看效果的去看[QUICK_START.md](https://awesome-embedded-learning-studio.github.io/imx-forge/QUICK_START)
125+
126+
想直接看驱动的去[字符设备驱动教程](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/driver/00_chardev_base/01_introduction)
127+
128+
想了解U-Boot的去[什么是U-Boot](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/uboot/01_what_is_uboot)
129+
130+
想了解内核的去[内核概述](https://awesome-embedded-learning-studio.github.io/imx-forge/tutorial/kernel/01_kernel_overview)
131+
132+
祝你在嵌入式Linux的世界里玩得开心!如果有问题随时去[GitHub Issues](https://github.com/Awesome-Embedded-Learning-Studio/imx-forge/issues)找我们!!

document/tutorial/start/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 章节
44

55
<ChapterNav>
6+
<ChapterLink num="00" href="00_roadmap">学习路线图 —— 嵌入式Linux是什么?</ChapterLink>
67
<ChapterLink num="01" href="01_start_from_toolchain">工具链安装 —— ARM GNU Toolchain 15.2</ChapterLink>
78
<ChapterLink num="02" href="02_env_init_guide">环境初始化指南</ChapterLink>
89
</ChapterNav>

project.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ export default defineProject({
5353
favicon: '/imx-forge/Awesome-Embedded.ico',
5454

5555
homeBanner: {
56-
'zh-CN': '教程具备时效性,嵌入式开发技术更新迅速,请自行对比时间参考。官方文档总是你的第一参考人。',
56+
'zh-CN': '🚀 新手必读:不知道从哪里开始?请先查看 <a href="/imx-forge/tutorial/start/00_roadmap">学习路线图</a>,了解嵌入式Linux的学习路径和项目结构。',
5757
},
5858
})

site/.vitepress/plugins/escape-cpp-templates.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const HTML_TAGS = new Set([
2929

3030
// Only skip actual registered Vue components, not all PascalCase patterns
3131
const VUE_COMPONENTS = new Set([
32-
'ChapterNav', 'ChapterLink', 'HomeTipBanner',
32+
'ChapterNav', 'ChapterLink', 'HomeTipBanner', 'Badge',
3333
'PageHeader', 'StatusTag', 'StepFlow', 'StepItem', 'InfoCard',
3434
'RoadMap', 'RoadMapPhase',
3535
'HomeArchDiagram',
@@ -48,6 +48,9 @@ function processLine(line: string): string {
4848
return segments.map((seg, i) => {
4949
if (i % 2 === 1) return seg
5050
return seg.replace(/<([^<>\n]+)>/g, (match, inner) => {
51+
// Extract tag name (first word) for Vue component check
52+
const tagName = inner.trim().split(/\s+/)[0]
53+
if (VUE_COMPONENTS.has(tagName)) return match
5154
return looksLikeCppTemplate(inner)
5255
? `&lt;${inner.trim()}&gt;`
5356
: match

site/.vitepress/theme/components/HomeTipBanner.vue

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@ const showBanner = computed(() => !!bannerText.value)
3535
.home-tip-banner {
3636
max-width: 1152px;
3737
margin: -16px auto 32px;
38-
padding: 12px 20px;
39-
border-radius: 8px;
38+
padding: 16px 24px;
39+
border-radius: 12px;
4040
display: flex;
4141
align-items: center;
42-
gap: 10px;
43-
background-color: var(--vp-tip-bg, rgba(83, 162, 83, 0.08));
44-
border: 1px solid var(--vp-tip-border, rgba(83, 162, 83, 0.2));
42+
gap: 12px;
43+
background: linear-gradient(135deg, rgba(83, 162, 83, 0.12) 0%, rgba(83, 162, 83, 0.06) 100%);
44+
border: 2px solid var(--vp-tip-border, rgba(83, 162, 83, 0.3));
4545
color: var(--vp-tip-text, var(--vp-c-text-2));
46-
font-size: 14px;
46+
font-size: 16px;
4747
line-height: 1.6;
48+
font-weight: 500;
49+
box-shadow: 0 2px 8px rgba(83, 162, 83, 0.15);
4850
}
4951
5052
.home-tip-icon {
@@ -54,14 +56,15 @@ const showBanner = computed(() => !!bannerText.value)
5456
align-items: center;
5557
}
5658
57-
.home-tip-text a {
58-
color: var(--vp-tip-accent, #53a253);
59+
.home-tip-text :deep(a) {
60+
color: #3b82f6;
5961
text-decoration: underline;
60-
text-underline-offset: 2px;
61-
font-weight: 500;
62+
text-underline-offset: 3px;
63+
font-weight: 700;
64+
transition: all 0.2s ease;
6265
}
6366
64-
.home-tip-text a:hover {
65-
color: var(--vp-tip-accent-hover, #3d7a3d);
67+
.home-tip-text :deep(a:hover) {
68+
color: #2563eb;
6669
}
6770
</style>

site/.vitepress/theme/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import InfoCard from './components/InfoCard.vue'
1313
import RoadMap from './components/RoadMap.vue'
1414
import RoadMapPhase from './components/RoadMapPhase.vue'
1515
import DocNavCards from './components/DocNavCards.vue'
16+
import projectConfig from '../../../project.config.ts'
1617
import './custom.css'
1718

1819
export default {
1920
extends: DefaultTheme,
2021
Layout() {
2122
return h(DefaultTheme.Layout, null, {
22-
'home-features-before': () => h(HomeTipBanner),
23+
'home-features-before': () => h(HomeTipBanner, { config: projectConfig }),
2324
'home-features-after': () => h(HomeArchDiagram),
2425
'doc-after': () => h(DocNavCards)
2526
})

0 commit comments

Comments
 (0)