2. Buildroot文件系统框架:BR2_TARGET_ROOTFS_* 配置体系,如何选择文件系统类型
好,咱们进入第二个章节。这一节我打算聊聊Buildroot里那个让人又爱又恨的配置体系——BR2_TARGET_ROOTFS_*。
说实话,我第一次接触这个配置项的时候,看着那一长串选项,心里直犯嘀咕:怎么这么多文件系统类型?选错了会不会把板子搞成砖?后来踩过几次坑,才慢慢摸清了门道。
2.1 配置体系概览:BR2_TARGET_ROOTFS_* 到底在管什么?
简单来说,BR2_TARGET_ROOTFS_* 就是Buildroot用来决定「最终生成什么格式的根文件系统镜像」的那组配置开关。你想想看,我们编译完内核、装好各种应用,总得有个东西能塞到Flash或者SD卡里让板子跑起来吧?对,就是它。
在 make menuconfig 里,你会在 Filesystem images 菜单下找到这些选项。它们长这样:
Filesystem images --->
[*] ext2/3/4 root filesystem
[ ] cramfs root filesystem
[ ] squashfs root filesystem
[ ] ubifs root filesystem
[ ] jffs2 root filesystem
[ ] tar root filesystem
[ ] cpio root filesystem
...
每个选项背后,都对应着一套完整的打包脚本和参数配置。你勾选了哪个,Buildroot就会在编译结束后,调用对应的工具链去生成那个格式的镜像。
核心要点:BR2_TARGET_ROOTFS_* 不是让你「选一个」,而是可以「选多个」。我见过不少新手以为只能单选,结果只生成了一种格式,后面调试时发现烧录工具不支持,又得重新编译——白白浪费一两个小时。
2.2 主流文件系统类型对比:选型前的必修课
咱们来看看最常见的几种文件系统类型。我按使用场景把它们分成了三类:
| 文件系统类型 | 适用存储介质 | 压缩支持 | 可写性 | 典型场景 |
|---|---|---|---|---|
| ext2/3/4 | SD卡、eMMC、硬盘 | 否 | 可读写 | 开发板、通用Linux |
| squashfs | Flash、SD卡 | 是(gzip/lzma/lzo) | 只读 | 嵌入式只读根文件系统 |
| ubifs | NAND Flash | 是(LZO/zlib) | 可读写 | NAND Flash设备 |
| jffs2 | NOR/NAND Flash | 是(zlib) | 可读写 | 老旧Flash设备 |
| cramfs | Flash、ROM | 是(zlib) | 只读 | 极小系统、老旧内核 |
| cpio | 内存(initramfs) | 否 | 内存中可写 | 早期启动、急救系统 |
| tar | 任意(打包格式) | 是(gzip/bzip2/xz) | N/A | 分发、备份、二次处理 |
这张表我建议你收藏一下。每次做新项目,拿出来对照着看,选型思路会清晰很多。
2.3 如何选择:我的实战决策流程
我个人习惯按下面这个顺序来决策:
- 先看存储介质——是NAND Flash、NOR Flash、eMMC还是SD卡?
- 再看容量限制——存储空间够不够?需不需要压缩?
- 最后看运行需求——根文件系统需要可写吗?还是只读就够了?
举个例子。我在做一个工业控制器的项目时,用的是128MB的NAND Flash。当时我第一反应是选jffs2,毕竟它支持NAND嘛。但后来发现jffs2在挂载大分区时慢得让人抓狂——128MB的分区,第一次挂载要等将近两分钟。后来我换成了ubifs,挂载时间缩短到几秒钟。嗯,这里要注意:ubifs需要内核开启UBI支持,而且对NAND的坏块管理更智能。
我的建议:
- 如果存储介质是 eMMC或SD卡,优先考虑 ext4。性能好,工具链成熟,调试方便。
- 如果存储介质是 NAND Flash,优先考虑 ubifs。jffs2只建议在老旧内核(2.6.x)上使用。
- 如果存储介质是 NOR Flash,jffs2 或 squashfs 都可以,看是否需要可写。
- 如果 空间极度紧张(比如只有4MB),squashfs 配合高压缩比算法是首选。
- 如果系统需要 从内存启动(比如急救模式),cpio 格式的 initramfs 是标配。
2.4 配置实战:在Buildroot中设置文件系统类型
好,理论说完了,咱们来点实际的。假设你手头有一个项目,用的是NAND Flash,需要可读写的根文件系统。我会这样配置:
# 进入配置界面
make menuconfig
# 进入文件系统镜像菜单
Filesystem images --->
[*] ubifs root filesystem
(0x20000) UBI logical erase block size # 根据NAND的页大小调整
(2048) UBI minimum I/O unit size # 通常等于NAND页大小
(1) UBI sub-page size # 有些NAND支持子页
(0x1f000) UBI physical erase block size # 通常等于NAND块大小
[*] tar root filesystem # 同时生成tar包,方便调试
这里有个坑,我曾经踩过:UBI的LEB(Logical Erase Block)大小如果设置不对,生成的ubifs镜像在烧录后根本挂载不上。怎么算?公式很简单:LEB = PEB - 2 * 页大小。比如你的NAND块大小是128KB(0x20000),页大小是2KB(0x800),那么LEB就是 0x20000 - 2 * 0x800 = 0x1F000。
注意:如果你同时勾选了多个文件系统类型,Buildroot会依次生成对应的镜像文件。但要注意,它们之间是独立的,不会互相影响。比如你同时选了ext4和squashfs,最终会得到 rootfs.ext4 和 rootfs.squashfs 两个文件。
2.5 选型避坑指南
做嵌入式存储优化这么多年,我总结了几条血泪教训:
- 别盲目追求压缩率——squashfs的zstd压缩比gzip高,但解压速度慢。如果你的CPU主频只有200MHz,解压时间可能比读取时间还长。
- 注意内核配置——选了ubifs,别忘了在内核里开启UBI和UBIFS支持。我见过有人折腾了半天ubifs镜像,结果内核根本没编译进去——白忙活。
- tar格式不是用来直接跑的——它只是个打包格式,方便你解压到目标分区。别想着把tar包直接当根文件系统用。
- cpio格式的initramfs有大小限制——早期内核可能只支持几MB的initramfs,太大了会启动失败。
我曾经在一个项目里,为了省那几百KB的空间,选了cramfs。结果发现cramfs不支持4KB以上的文件,而且最大文件系统大小只有256MB。后来产品迭代,固件体积涨到了300MB,cramfs直接废了,不得不重新选型。所以我的建议是:除非你的系统真的小到极致,否则别碰cramfs,squashfs是更好的选择。
2.6 小结
这一节我们聊了Buildroot的BR2_TARGET_ROOTFS_*配置体系。说白了,选文件系统类型就是三个问题:用什么存?空间多大?要不要写?想清楚这三点,选型就不难了。
下一节我会深入讲讲文件系统的裁剪技巧——怎么把根文件系统从几十MB砍到几MB,同时保证功能完整。到时候我会拿一个实际项目做案例,一步步演示裁剪过程。