文件系统基础:核心概念与VFS接口

好,咱们今天来聊聊文件系统的核心概念。说实话,这部分内容我当年学的时候也觉得挺抽象的,什么inode、超级块,听着就头大。但后来真正动手做微内核项目时才发现,这些概念就像房子的地基——你看着不起眼,少了它整个系统都得塌。

inode:文件的“身份证”

先说说inode。我习惯把它理解成文件的“身份证”。每个文件都有一个唯一的inode号,就像你的身份证号一样。但inode里存的是什么呢?

  • 文件类型:普通文件、目录、符号链接等
  • 权限位:rwx那些东西
  • 文件大小:字节数
  • 时间戳:创建时间、修改时间、访问时间
  • 数据块指针:指向实际数据存放位置的指针数组

这里有个关键点——文件名并不在inode里。文件名存在哪儿?存在目录项里。这个设计我一开始也觉得奇怪,后来才明白:这样就能实现硬链接了。多个文件名指向同一个inode,数据只有一份。

核心要点:inode是文件系统管理文件元数据的核心结构。它不存文件名,只存文件的“属性”和“数据位置”。

我在项目中遇到过一个问题:一个嵌入式设备上,明明磁盘空间还有,但就是创建不了新文件。查了半天,发现是inode用完了。嗯,每个文件系统格式化时inode数量就固定了,这点要特别注意。

超级块:文件系统的“户口本”

超级块(Superblock)是整个文件系统的元数据。它记录了文件系统的全局信息:

字段 说明
魔数 文件系统类型标识,比如EXT4的魔数是0xEF53
块大小 通常是1024、2048或4096字节
inode总数 格式化时确定的inode数量上限
空闲块数 当前可用的数据块数量
挂载时间 最后一次挂载的时间戳

说白了,超级块就是文件系统的“户口本”。你想想看,操作系统要挂载一个文件系统,第一件事就是读超级块,确认这玩意儿是什么格式、有多大、状态是否正常。

注意:超级块损坏,整个文件系统就废了。所以很多文件系统会在磁盘不同位置备份超级块。我曾经手贱直接写坏了一个U盘的超级块,里面的数据全没了……从那以后我再也不敢直接操作原始设备了。

目录项:文件名与inode的“桥梁”

目录项(dentry,Directory Entry)把文件名和inode号关联起来。每个目录项就是一个映射关系:

struct dentry {
    char name[256];      // 文件名
    uint32_t inode_num;  // 对应的inode号
    struct dentry *parent; // 父目录项
    struct dentry *child;  // 子目录项链表
};

目录本身也是一个文件,它的数据块里存的就是一堆目录项。所以当你执行 ls /home 时,内核实际上是在读 / 这个目录文件,找到 home 这个目录项,然后读 /home 目录文件的数据块。

我个人的习惯是,把目录项看作一个“缓存层”。它不一定是持久化存储的,很多文件系统在内存中动态构建目录树。这样做的好处是查找速度快,坏处是……嗯,如果内存不够,目录树重建会有点慢。

VFS:统一接口的“翻译官”

VFS(Virtual File System,虚拟文件系统)是Linux内核中最巧妙的设计之一。它的作用很简单:让上层应用不用关心底层文件系统是什么格式。

你想想看,你的程序调用 open()、read()、write(),这些函数怎么知道底层是EXT4还是FAT32?答案就是VFS。

VFS定义了一套标准接口:

struct file_operations {
    int (*open)(struct inode *, struct file *);
    ssize_t (*read)(struct file *, char *, size_t, loff_t *);
    ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
    int (*release)(struct inode *, struct file *);
};

每种文件系统都要实现这套接口。比如EXT4有ext4_file_operations,FAT32有fat_file_operations。VFS就像一个翻译官,把通用的系统调用翻译成具体文件系统的操作。

小技巧:在微内核中实现文件系统时,我建议先把VFS接口层写好。这样后面换文件系统实现,上层代码完全不用动。我在一个IoT项目里就是这么干的,后来从FAT32换成SPIFFS,只改了底层驱动,应用层一行代码没动。

三者的关系

来,我画个图帮你理清思路:

应用程序 VFS(虚拟文件系统) EXT4 FAT32 超级块 inode表 目录项 数据块

从图上你能看到:应用程序通过VFS调用具体文件系统,具体文件系统又依赖超级块、inode、目录项这些核心结构来管理数据。每一层各司其职,这就是微内核文件系统的设计哲学。

总结一下

嗯,今天的内容就这些。我帮你捋一遍:

  • inode:存文件属性,不存文件名
  • 超级块:存文件系统全局信息,相当于户口本
  • 目录项:连接文件名和inode,是查找的关键
  • VFS:统一接口,让上层不用关心底层格式

下一章我们会动手实现一个简单的VFS层。到时候你会发现,理解了这些概念,写代码其实没那么难。有什么问题随时问我,咱们下节课见。