存储介质与分区基础
大家好,我是你们的讲师。这一章咱们聊聊嵌入式开发里最基础、也最绕不开的话题——存储介质和分区。说白了,就是你的代码到底存在哪、怎么存、怎么组织。我见过不少新手,上来就烧录,结果板子变砖,十有八九是没搞懂这层。
Flash存储器原理
Flash存储器,你可以把它想象成一块电子黑板。写数据就像用粉笔写字,擦除就像用板擦擦掉。但有个关键区别:Flash只能把1写成0,不能把0写成1。想改回1?必须整块擦除。
为什么会这样?这跟Flash的物理结构有关。每个存储单元是一个浮栅晶体管,写入时注入电荷,擦除时释放电荷。电荷注入是逐位操作的,但释放必须整块来。嗯,这里要注意,这个“块”就是后面要说的“擦除块”,大小通常是64KB或128KB。
我在项目中遇到过一个问题:某款IoT设备频繁写日志,结果Flash提前报废了。原因就是没考虑Flash的擦写寿命——一般NOR Flash能撑10万次,NAND Flash只有几千到几万次。所以,别把日志直接往Flash上怼,要用磨损均衡算法。
NAND vs NOR Flash
这两种Flash,就像卡车和跑车,各有各的赛道。我做个表格,大家一看就明白。
| 特性 | NOR Flash | NAND Flash |
|---|---|---|
| 读取速度 | 快(支持随机读取,类似RAM) | 较慢(只能按页读取) |
| 写入速度 | 慢 | 快(批量写入效率高) |
| 擦除速度 | 慢 | 快 |
| 密度 | 低(最大几百MB) | 高(可达几TB) |
| 可靠性 | 高(坏块少) | 低(出厂就有坏块,需要ECC) |
| 典型用途 | 代码存储、启动引导 | 大容量数据存储 |
| 成本 | 高 | 低 |
我个人习惯:NOR Flash用来放Bootloader和关键代码,因为可以XIP(片上执行),CPU直接读,不用先拷到RAM。NAND Flash则用来放文件系统、用户数据。你想想看,手机里的存储芯片基本都是NAND,但它的启动ROM还是NOR。
分区表概念
分区表,就是一张地图,告诉系统:哪块区域是干嘛的。没有分区表,Flash就是一块大荒地,你都不知道哪里能种菜、哪里能盖房。
分区表通常放在Flash的固定偏移位置,比如偏移0处。它包含每个分区的:
- 名称:比如boot、system
- 起始地址:在Flash中的偏移
- 大小:分区占用的字节数
- 属性:是否可读、可写、可执行
下面是一个典型的分区表结构图,我用SVG画出来,大家感受一下。
这张图展示了一个典型的嵌入式Linux分区布局。注意,分区表本身也占一个分区,而且通常放在最前面。我建议你设计分区时,给每个分区留点余量,别算得死死的。我曾经因为分区大小刚好够,结果系统更新时新固件大了几KB,直接刷爆了。
常见分区类型
咱们逐个看看这些分区是干嘛的。
Boot分区
这个分区存放的是启动引导程序,比如U-Boot、GRUB。它负责初始化硬件、加载内核。我一般把它放在NOR Flash里,因为启动时CPU直接就能读。如果放在NAND里,Bootloader自己得先初始化NAND控制器,有点蛋生鸡的问题。
System分区
系统分区,存放操作系统内核、驱动、核心库。在Android设备里,这就是/system目录。这个分区通常是只读的,防止用户误删系统文件。我遇到过有人手贱删了/system下的某个so文件,结果系统直接起不来。
Data分区
用户数据分区,存放应用、配置、用户文件。这个分区是可读写的,也是磨损最严重的。我建议你在这个分区上启用文件系统日志,比如ext4的journal,防止突然断电导致数据损坏。
Cache分区
缓存分区,存放临时数据,比如系统更新包、应用缓存。这个分区可以随时清空,不影响系统运行。嗯,这里要注意,Cache分区的大小不要太大,一般几十MB就够了。我见过有人给Cache分了1GB,纯属浪费。
分区表代码示例
下面是一个实际的分区表定义,来自某个嵌入式Linux项目。我用的是设备树(Device Tree)格式。
// 分区表定义 (设备树格式)
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "bootloader";
reg = <0x000000 0x100000>; // 1MB
read-only;
};
partition@1 {
label = "boot";
reg = <0x100000 0x200000>; // 2MB
read-only;
};
partition@2 {
label = "system";
reg = <0x300000 0x500000>; // 5MB
read-only;
};
partition@3 {
label = "cache";
reg = <0x800000 0x100000>; // 1MB
};
partition@4 {
label = "data";
reg = <0x900000 0x700000>; // 7MB
};
};
这段代码定义了5个分区,总大小16MB。注意看地址是连续的,从0开始。我习惯把Bootloader放在最前面,因为CPU复位后默认从地址0开始执行。
好了,这一章的内容就到这里。Flash的原理、NAND和NOR的区别、分区表的概念、常见分区类型,这些是后续烧录实战的基础。你把这些搞懂了,后面烧录的时候就不会一脸懵了。