4、MTD分区框架:MTD子系统介绍、mtdparts命令详解、分区描述符格式
说到嵌入式Linux的存储分区,MTD(Memory Technology Device)是个绕不开的话题。我最早接触MTD是在做一款工业级路由器的时候,当时被Flash分区搞得焦头烂额——U-Boot传参、内核解析、文件系统挂载,每个环节都可能出问题。后来把MTD这套机制吃透了,才算是真正掌握了分区管理的主动权。
4.1 MTD子系统:存储设备的抽象层
MTD子系统,说白了就是Linux内核给Flash类存储设备搭的一个统一框架。它把NOR Flash、NAND Flash、SPI Flash这些底层硬件差异给屏蔽掉了,向上提供一套标准化的接口。
为什么要搞这么一层?你想想看,NOR Flash可以按字节访问,NAND Flash只能按页读写,SPI Flash又是个串行协议。如果没有MTD,驱动工程师得为每种Flash写一套完全不同的分区管理代码——那画面太美我不敢看。
MTD子系统的核心架构分三层:
- 底层驱动层:直接操作硬件,实现擦除、读写、坏块管理等基础操作
- MTD核心层:提供设备注册、分区管理、字符/块设备接口
- 上层用户层:通过
/dev/mtdX字符设备或/dev/mtdblockX块设备访问
我在项目中遇到过一个问题:某款NAND Flash的坏块管理在MTD层和文件系统层都做了,结果导致坏块标记重复,数据越写越乱。后来统一在MTD层处理坏块,上层只管读写,问题就解决了。
关键点:MTD分区是逻辑上的划分,不是物理上的切割。同一个物理Flash芯片,可以划分成多个MTD分区,每个分区独立管理。
4.2 mtdparts命令:U-Boot里的分区利器
在U-Boot环境下,mtdparts命令是我们操作MTD分区的主要工具。我记得第一次用这个命令时,被它的参数格式搞得晕头转向——又是mtd-id又是size@offset,稍不注意就写错了。
mtdparts命令的核心功能就三个:
- 查看分区表:
mtdparts(不带参数)显示当前所有MTD设备的分区信息 - 设置分区表:
mtdparts default恢复默认分区,mtdparts add添加新分区 - 保存分区表:通过
saveenv将分区配置保存到环境变量中
实际使用中,我一般这样操作:
# 查看当前分区
U-Boot> mtdparts
# 设置分区(假设Flash总大小为16MB)
U-Boot> setenv mtdparts 'mtdparts=nand0:1M(u-boot),4M(kernel),8M(rootfs),3M(data)'
U-Boot> saveenv
U-Boot> mtdparts
这里有个坑——mtdparts环境变量的格式必须和内核的MTD_PARTITIONS配置严格对应。我曾经因为分区名里多了一个空格,导致内核解析失败,整块板子起不来。嗯,从那以后我写分区字符串都格外小心。
注意:mtdparts命令依赖于U-Boot的CONFIG_CMD_MTDPARTS配置。如果编译时没打开这个选项,命令会提示"Unknown command"。
4.3 分区描述符格式:写对才能用对
分区描述符的格式,说白了就是告诉系统"哪块区域归谁用"。标准格式如下:
mtdparts=<mtd-id>:<size>[@<offset>](<name>)[<ro>]
各字段含义:
| 字段 | 说明 | 示例 |
|---|---|---|
mtd-id |
MTD设备标识,如nand0、spi0.0 |
nand0 |
size |
分区大小,支持K/M/G后缀,-表示剩余全部 |
4M、1G、- |
offset |
起始偏移,可省略(自动接续上一个分区末尾) | @0x100000 |
name |
分区名称,用于标识 | kernel |
ro |
只读标志,可选 | ro |
举个完整的例子:
mtdparts=nand0:1M(u-boot)ro,4M(kernel),8M(rootfs),3M(data)
这个配置的意思是:
- nand0设备上,第一个分区1MB,只读,放U-Boot
- 第二个分区4MB,放内核
- 第三个分区8MB,放根文件系统
- 第四个分区3MB,放用户数据
注意,这里没有指定偏移量,分区会自动从0地址开始连续排列。如果你需要跳过某些区域(比如坏块保留区),可以手动指定偏移:
mtdparts=nand0:1M(u-boot)ro,@0x2000004M(kernel)
我个人习惯在分区名里加上版本号或用途标记,比如kernel_v2、rootfs_ro,这样在调试时一眼就能看出哪个分区在起作用。
小技巧:如果Flash总大小不确定,可以用-表示"占满剩余空间"。比如mtdparts=nand0:1M(u-boot),-(data),这样data分区会自动吃掉所有剩余空间。
4.4 知识体系总览
下面这张图是我自己整理的MTD分区框架核心逻辑,从U-Boot到内核再到用户空间,整个链路一目了然:
从这张图可以看得很清楚:U-Boot负责定义分区,内核负责解析和注册,用户空间负责使用。三个环节环环相扣,任何一个地方出错都会导致分区不可用。
最后说一句,MTD分区表的设计一定要提前规划好。我见过太多项目因为分区大小预留不足,后期要升级内核却发现空间不够,只能重新烧写整个Flash——那叫一个痛苦。所以,宁可多留点余量,也别抠抠搜搜的。