1. 文件系统基础:什么是文件系统?Linux文件系统层次标准(FHS)与VFS虚拟文件系统架构
大家好,我是你们的嵌入式系统讲师。今天咱们来聊聊文件系统——这个看似基础、但实际坑特别多的东西。
说实话,我刚开始做嵌入式开发那会儿,对文件系统的理解就是「存文件的地方」。直到有一次,我在一个资源受限的MCU项目上,因为选错了文件系统,导致系统频繁死机……嗯,从那以后,我才真正开始认真研究文件系统的底层逻辑。
1.1 什么是文件系统?
文件系统,说白了就是一套管理数据的规则。它决定了数据怎么存、怎么找、怎么删。
你想想看,硬盘或者Flash只是一堆0和1。如果没有文件系统,你根本不知道哪个字节属于哪个文件。文件系统就是给这些原始数据「贴标签」的机制。
我个人习惯把文件系统比作一个图书馆:
- 数据块 = 书架上的格子
- 文件名 = 书的标题
- 目录结构 = 图书馆的分类索引
- 元数据 = 书的出版信息、作者、页数
文件系统要干的事,就是管理这些「格子」和「索引」。常见的文件系统有:
- FAT32:老古董了,但兼容性极好。U盘、SD卡标配。
- ext4:Linux桌面和服务器的主流选择。
- JFFS2 / YAFFS2:专门为NAND Flash设计的,我早期做路由器项目时常用。
- SquashFS:只读压缩文件系统,嵌入式固件里很常见。
核心要点:文件系统不是「一个」东西,而是一类东西。选型时一定要看硬件介质和性能需求。
1.2 Linux文件系统层次标准(FHS)
FHS,全称Filesystem Hierarchy Standard。它规定了Linux系统里各个目录该放什么。
我记得刚入行时,有个同事把自定义脚本扔到了/usr/bin下,结果系统升级时全被覆盖了。这就是不懂FHS的后果。
FHS的核心目录结构如下:
| 目录 | 用途 | 我的经验 |
|---|---|---|
/bin |
用户基本命令(ls, cp, mv) | 嵌入式里常是busybox的软链接 |
/sbin |
系统管理命令(fdisk, mkfs) | 启动时就需要,不能放/usr下 |
/etc |
配置文件 | 我习惯把自定义配置单独放/etc/custom/ |
/dev |
设备文件 | 现在都用udev动态管理了 |
/proc |
进程和内核信息(虚拟文件系统) | 调试时经常cat /proc/cpuinfo |
/sys |
内核对象信息(sysfs) | 驱动开发时必看 |
/tmp |
临时文件 | 嵌入式里常挂tmpfs,掉电不保留 |
/var |
可变数据(日志、缓存) | 注意日志别写爆了Flash |
/usr |
用户程序和数据 | 可以单独分区,只读挂载 |
/home |
用户家目录 | 嵌入式里基本用不到 |
小技巧:在嵌入式系统里,我经常把/usr和/opt做成只读分区,防止意外写坏。而/var和/tmp则用tmpfs,掉电自动清空,省心。
1.3 VFS虚拟文件系统架构
VFS,Virtual File System。这是Linux内核里最巧妙的设计之一。
为什么要搞VFS?因为不同的文件系统(ext4、FAT32、JFFS2)底层实现完全不同。如果没有VFS,你写个open()函数,就得针对每种文件系统写一套代码——那得多累?
VFS的作用就是「统一接口」。它定义了一套标准操作:
open()、close()、read()、write()mkdir()、rmdir()、unlink()stat()、chmod()、chown()
你调用这些函数时,VFS会帮你把请求转发给具体的文件系统驱动。你根本不用关心底层是Flash还是硬盘。
VFS的核心数据结构有四个:
- 超级块(super_block):描述整个文件系统的信息,比如块大小、挂载状态。
- 索引节点(inode):描述一个文件或目录的元数据(权限、大小、位置)。
- 目录项(dentry):描述文件名和inode的对应关系。说白了就是「目录缓存」。
- 文件对象(file):描述一个打开的文件实例,包含当前读写位置。
重点:VFS让Linux可以同时挂载多个不同类型的文件系统。比如你的/是ext4,/boot是FAT32,/mnt/usb是NTFS——这在VFS看来毫无违和感。
我曾经调试过一个性能问题:系统在大量小文件读写时卡顿。后来发现是VFS的dentry缓存太小,导致频繁查找目录项。调大dentry缓存后,性能直接翻倍。嗯,这就是VFS调优的实战价值。
1.4 文件系统架构图
下面我用一张SVG图来展示VFS、具体文件系统和硬件之间的关系:
这张图很直观:用户程序通过系统调用进入VFS,VFS根据挂载点找到对应的文件系统驱动,驱动再通过块设备层操作硬件。每一层各司其职,互不干扰。
1.5 避坑指南
我曾经踩过的坑:
- 在NAND Flash上直接挂载ext4——结果频繁坏块导致文件系统崩溃。后来改用JFFS2,问题解决。
- 把日志直接写到
/var/log,没做日志轮转。结果Flash被写爆,系统无法启动。 - 在VFS层做大量
stat()调用,没开dentry缓存。性能惨不忍睹。
我的建议:
- 嵌入式系统优先考虑只读文件系统(SquashFS)配合tmpfs使用。
- 需要写操作时,选支持磨损均衡的文件系统(JFFS2、UBIFS)。
- 调试时多用
strace跟踪系统调用,看VFS层到底在干什么。
好了,文件系统的基础就讲到这里。记住一句话:文件系统选型,决定了你系统的稳定性和性能上限。别等到出问题了才后悔。
课后思考:为什么Linux的/proc和/sys被称为「虚拟文件系统」?它们和普通文件系统有什么本质区别?