作为嵌入式开发,日常打交道最多的就是ext系列文件系统。无论是SD卡启动镜像、eMMC的rootfs分区,还是刚买回来的U盘要存大文件,背后都是这套Linux世界的“老伙计”在干活。最近看到好几个技术群在聊U盘拷不进单个4G以上文件、Windows下插上ext4硬盘直接“抓瞎”、chkdsk提示RAW分区这类问题,其实根源都落在“文件系统选型和底层格式理解”上。这篇不聊虚的,直接从ext家族谱系讲起,把目录结构、inode、块分配、挂载与VFS的关系,再到跨系统读写、RAW故障恢复、嵌入式根文件系统编译这些实操场景逐一掰开,希望能帮正在被文件系统折磨的朋友理清思路。
1. 从一张软盘到PB级存储:ext为什么要一直“换代”
理解ext系列,第一条主线是“每一代ext都在解决当时最疼的问题”。很多初学者盯着mkfs.ext4和mkfs.ext3的命令差异看半天,却不明白为什么会有这些差异,结果换个场景就不知道怎么选型。这里我把历史线串一遍。
1.1 ext2定下的“老规矩”:inode与块的世界
ext2诞生于1993年,是Linux真正意义上第一个“工业化”的文件系统。它的核心设计思路后来一直延续到ext4:把存储空间切成固定大小的块(block),每个文件用一个叫inode的结构体记录元数据(大小、权限、时间戳、数据块指针),文件名则放在目录文件里。目录本身也是一个文件,里面存的是“文件名到inode编号”的映射表。
所以Linux下一切皆文件,目录就是特殊文件,这个说法不是修辞,而是ext系列底层的真实数据结构。你在终端执行ls -li看到的第一列数字就是inode号,两个文件inode号相同,说明它们是硬链接,指向同一份数据。
ext2的块大小在格式化时可选1KB、2KB、4KB。块越小,磁盘空间利用率越高,但大文件需要更多块指针,寻址开销大;块越大,大文件性能好,小文件则浪费空间。这个“块大小焦虑”一直到ext4都没有消失,后续会细说。
ext2的局限很明显:突然断电没有日志保护,长时间运行后文件碎片增多,单文件最大只能2TB(块大小4KB时),分区最大16TB。这些瓶颈驱动了ext3和ext4的诞生。
1.2 ext3带来的“记账本”机制
ext3是ext2加了个日志(journal)功能,用术语讲叫“ext2 with journaling”。日志区域独立划分出来,文件系统在做任何结构性修改前,先把操作记到日志里,再动真正的位图和inode表。这样系统崩溃后重启时,只要回放日志就能把文件系统恢复到一致状态,不用对整个磁盘做全面扫描。
打个比方:ext2像是个没有备忘录的账房先生,突然被打断就完全不记得账记到哪一步;ext3则是每做一笔账之前先在小本子上写“我要做什么”,做完再划掉。本子就是日志,回放就是按本子重做未完成的事情。
在嵌入式场景里,ext3的日志区默认放在同一个块设备上。如果板子频繁掉电,日志区和数据区都在同一块eMMC上,理论上还是存在数据不一致窗口,所以后面有了ext4的延迟分配、校验和、多块分配等改良,还有专门针对嵌入式需求的ext3日志模式选项(journal, ordered, writeback)。默认ordered模式保证数据先落盘、元数据再落日志,是安全性和性能最均衡的选择。
1.3 ext4:一次“攒够了”的大版本升级
ext4在2008年进入内核主线,与其说是革命,不如说是把多年来积累的补丁和机制做了系统性落地。几个核心变化对日常使用影响巨大:
- 支持卷容量最大1EiB(块大小4KB时),单个文件最大16TiB,磁盘阵列、大容量存储都没压力。
- extents(区段)取代传统块映射,一个inode用一棵树记录连续区域的起始块号和长度,大文件的地址映射空间大幅缩小,读写大文件时块查找效率提升。
- 多块分配(mballoc)在写文件时一次分配多个块,减少碎片化。
- 延迟分配(delayed allocation)让文件先缓存数据到page cache,攒够了一定量的脏页再落盘,显著减少小块写入次数。
- 日志校验和、快速恢复、在线defrag、纳秒时间戳、预分配等特性一起补齐。
当时发布会给的最直观数据是:修复碎片的时间大大缩短,且最大文件从2TB到16TiB,单个卷从16TB到1EiB。这个“4”一直用到现在,15年过去没有ext5立项的消息,说明架构本身已经够成熟,后续改良都在功能开关层面,比如casefold(大小写不敏感)、fast_commit(快速提交)这些feature flag。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 目录系统与文件数据是怎么“长”出来的:从inode解析到ls的功夫
很多人在Windows下资源管理器用习惯了,双击进目录看到文件就行。但ext系列的目录组织是个B树变种(早先ext2/3是线性链表,ext4引入Htree索引),文件多了以后查找速度依然可控,这是ext能撑住大目录的原因。花点时间理解树形组织,对于后面做文件系统镜像分析、恢复误删数据都会特别有用。
2.1 一棵树的搭建:inode表、块位图与目录项
格式化完一个ext4分区,会生成:
- 超级块(superblock):记录文件系统全局信息(块总数、inode总数、特性标志、挂载次数等)。前面几个块的备份副本对fsck修复至关重要。
- 块组(block group):把整个分区切成一组一组,每个块组有自己的块位图、inode位图、inode表和该组的数据块。这样文件系统可以局部化分配,也方便检查和修复。
- inode区:固定大小的inode阵列,每个inode占256字节(默认),记录文件的所有属性和数据块索引。
新文件创建流程大致是:先在块组里找到空闲inode,写入文件元数据;再在目录文件里新增一条“文件名→inode号”的记录;最后为文件数据分配datablock并把块号填进inode的块列表。这个流程中任何一步掉电中断,都是需要fsck来定位的。
对恢复场景很有用的一个事实:删除文件时,默认只是把目录项里的inode号标记为“空闲”,数据块位图释放,inode里的元数据标记为“已删除”,但原来的数据块内容不会立刻被清零。所以“误删后马上停止写入、用debugfs或extundelete抢数据”是可行的,原理就在这里。一旦新文件覆盖了这些块,神仙也难救。
2.2 ls的执行路径:VFS、目录缓存与readdir的系统调用链路
你在终端输入ls,看似简单,内核里走的路径相当长:ls调用readdir()系统调用→VFS层的 iterate_dir()→ 具体文件系统的 directory opera
