嵌入式 Linux 开发 8:部署 MTD 设备
MTD 与 Flash
Linux 工作站和服务器一般使用磁盘作为存储设备,而嵌入式 Linux 倾向使用 Flash 固态存储设备。
相比磁盘的“读”和“写”操作,Flash 还需要“擦除”,并且它还有写寿命(常见为 10 万次)。
为了更好地操作 Flash 设备 Linux 添加了 MTD 子系统(Memory Technology Device)。
MTD@Flash 和 磁盘块设备 的区别如下表所示
操作单位 | 操作 | 耗损均衡 | |
---|---|---|---|
MTD | 擦除块 | 读/写/擦除 | 均匀分布写块 |
块设备 | 扇区 | 读/写 | / |
小窍门1
一般 MTD 设备指 Flash 芯片,常见的 SD/MMC CompactFlash USB闪存盘以及其他一些类似的设备更像传统的块设备(如磁盘)。
小窍门2
一般 Flash 分为 2 种类型 NOR 和 NAND,它们的区别如下表所示。
类别 | 容量 | 寻址单位 | 适合文件系统 | 管理坏块 | 性能 |
---|---|---|---|---|---|
NOR | 小 | 字节 | JFFS2 | / | 写速度快 |
NAND | 大 | 页 | YAFFS2 | 记录坏块 | 读速度快 |
内核支持 MTD
内核选项 | MTD 功能 |
---|---|
CONFIG_MTD | 支持 MTD 子系统 |
CONFIG_MTD_CONCAT | 将多个 MTD 设备或分区组合成单一文件系统 |
CONFIG_MTD_PARTITIONS | 支持 MTD 分区 |
CONFIG_MTD_CHAR | 通过 /dev/mtdN 和 /dev/mtdrN 操作 MTD 字符设备 |
CONFIG_MTD_BLOCK | 通过 /dev/mtdblockN 操作 MTD 块设备 |
CONFIG_MTD_CMDLINE_PARTS | 在命令行中传递分区信息 |
操作 MTD 分区
- 查看 MTD 分区
cat /proc/mtd
- 擦除 MTD 分区
flash_eraseall /dev/mtdX
- 写 MTD 分区 NOR Flash
flashcp /tmp/mtd.bin /dev/mtdX
- 写 MTD 分区 NAND Flash
nandwrite /tmp/image.bin /dev/mtdX
- 读 MTD 分区
dd if=/dev/mtdX of=/tmp/mtd.bin
MTD 与文件系统
为了克服 Flash 自身的 2 个限制:擦除块耗时易导致断电丢失数据,写块有寿命限制。
支持它的文件系统一般需要具备:断电可靠性 和 耗损平衡(wear leveling 尽可能将数据均匀散布在 Flash 上)
常见的 MTD 文件系统包括:JFFS2 UBIFS YAFFS2 详情请链接Linux 文件系统类型
MTD 分区
和磁盘不同,不能使用 fdisk 之类的工具给 MTD 分区。
一般而言 MTD 分区有如下 3 种办法
- 在内核命令行中定义分区表
- 使用与具体板卡相关的映射驱动
- 解析 Redboot TI_AR7 分区表
在 u-boot 引导的系统中使用方法 1 最简单;方法 2 需要修改 kernel 源代码并编译;方法 3 依赖特定的引导程序。
下面基于方法 1 给 MTD 分区
有一 128M NAND Flash 需要分成 5 个区,如下表所示
区块 | 分区 1 | 分区 2 | 分区 3 | 分区 4 | 分区 5 |
---|---|---|---|---|---|
名称 | u-boot | kernel | rootfs | config | log |
大小 | 2M | 20M | 40M | 2M | 64M |
按上面分区信息定义如下 u-boot 的参数,在启动时传递给内核,它会在 MTD 子系统中自动分区。
setenv bootargs 'noinitrd root=/dev/mtdblock2 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0 rdinit=/sbin/init mem=64M mtdparts=nand0:2M(u-boot),20M(kernel),40M(rootfs),2M(config),-(log) ignore_loglevel'
启动 Linux 系统后,执行命令 cat /proc/mtd
即可查看 MTD 分区。
dev: size erasesize name
mtd0: 00200000 00020000 "u-boot"
mtd1: 01400000 00020000 "kernel"
mtd2: 02800000 00020000 "rootfs"
mtd3: 00200000 00020000 "config"
mtd4: 04000000 00020000 "log"
编译与部署 MTD 工具
背景
为了升级 Linux 镜像,必然需要擦除/烧写 Flash,这是由 MTD 工具实现,它依赖 lzo libuuid 库。
编译 lzo 库
cd ${PRJROOT}/build-tools/lzo-2.09
./configure CC=${CROSS}-gcc --host=$CROSS --enable-shared
make
make prefix=$TARGET_PREFIX install
编译 libuuid 库
cd ${PRJROOT}/build-tools/libuuid-1.0.3
./configure --host=$CROSS
make
make prefix=$TARGET_PREFIX install
编译 MTD 工具
cd ${PRJROOT}/build-tools/mtd-utils
make CROSS="${CROSS}-" CFLAGS="-I ${TARGET_PREFIX}/include" LDFLAGS="-L ${TARGET_PREFIX}/lib"
部署 MTD 工具
cd $CROSS
MTD_TOOLS="./mtd_debug ./nandtest ./flash_erase ./nandwrite"
${CROSS}-strip $MTD_TOOLS
cp -vf $MTD_TOOLS <target_rootfs_dir>/usr/sbin/
大部分项目只需要上面 4 个 MTD 工具即可。幸运的是,它们只依赖 2 个库文件:libc(C 程序库) 和 ld(动态链接器)。
这 2 个库文件是基础软件(没有它们 Linux 几乎无法运行)。因此,不需要复制库文件到目标系统。