第八十一章:古树之森
斩灵期涉及内核源码:
一
林小源踏入了一片无边无际的古老森林。
参天巨木遮天蔽日,树冠连成一片墨绿的穹顶。脚下是厚厚的落叶,每踩一步都发出细碎的沙沙声。空气中弥漫着腐殖土和松脂的气息,偶有鸟鸣从远处传来,却看不到任何飞鸟的影子。
他伸手触碰最近的一棵树。树皮粗糙,纹路纵横交错,像某种古老的铭文。树干上刻着一串数字——"inode 2"。
"你是什么?"林小源低声问道。
树干微微震颤,一个苍老而浑厚的声音从树心传出:"我是这棵古树的元数据。权限、大小、时间戳、数据块的位置——都刻在我的躯干里。"
林小源绕着树走了一圈,忽然注意到一件事:"等等……你的名字呢?你叫什么?"
古树发出一声低沉的笑:"我没有名字。名字不在树上,在路标上。"
"路标?"
"dentry,"古树说,"它们站在路口,手里举着牌子。牌子上写着名字,箭头指向我们。一棵树可以有多个路标指着它——同一个 inode,不同的名字。"
林小源还想追问,脚下的落叶突然震动起来。他低头一看,落叶之间有一条条细密的根须在蠕动,根须汇聚的方向,是一块巨大的石碑。
石碑上刻着"superblock"三个字,表面覆盖着青苔。石碑散发出微弱的光芒,像一颗沉睡的心脏。
"那是什么?"
"守护灵,"古树的声音变得恭敬起来,"每一片森林都有一个守护灵。它记载着整片森林的结构——块大小、树的数量、挂载的标志。没有它,这片森林就不存在。"
林小源走近石碑,伸手触摸。一股温热的能量从指尖涌入,他隐约感知到了整片森林的轮廓——无数棵古树排列成整齐的方阵,方阵之间有道路相连。
"这就是 VFS,"他喃喃道。
"不错,"古树说,"无论你面前的是 ext4 的森林、btrfs 的森林,还是 procfs 的幻境——守护灵都为它们提供相同的规矩。open、read、write、close——四个字,走遍天下。"
二
林小源在古树之间穿行,越走越深。
林间的光线越来越暗,但每棵树干上的铭文却越来越亮,像是某种自发光的苔藓。他停下脚步,发现前方有一棵格外粗壮的古树,树干上密密麻麻刻满了标记。
"你和其他树不一样,"林小源说。
"我是目录,"古树回答,声音比之前那棵更年轻,也更急促,"我的躯干里没有数据块,只有子树的索引。你要找文件?先过我这关。"
林小源伸手按在树干上,感知到了一组指针——每一个指针都指向另一棵古树,指针旁边刻着名字。"home""user""file.txt"——名字和 inode 号一一对应。
"这就是 dentry 的作用,"林小源说。
"对,"目录树说,"你记住一件事——文件名不长在 inode 上,长在 dentry 上。一个 inode 可以有十个名字,但躯干只有一副。你 rm 一个文件,减的不是树,是路标。路标清零了,树才会倒。"
林小源正要开口,忽然感觉到一股异样的波动从森林深处传来。他循着波动走了一段路,发现一棵古树正在缓缓枯萎。树干上的铭文暗淡下去,数字"i_nlink"变成了零。
"它要倒了,"身后传来一个低沉的声音。
林小源回头,看到一个半透明的光影——那是一个 file 对象。它没有实体,像一团凝固的烟雾,烟雾中隐约可以看到一个数字"3"在闪烁。
"我是进程打开的文件,"光影说,"fd 3。这棵树的 inode 还活着,因为还有别的路标指着它。但我的引用计数已经归零了——我要消散了。"
话音刚落,光影化作一缕青烟,消散在林间。
三
林小源继续深入森林。
天色彻底暗了下来,但森林并不黑暗——每棵树都在发出微弱的荧光,像无数盏灯笼悬浮在夜色中。他循着荧光走了很久,终于来到一片空地。
空地中央有一棵倒下的古树。树干已经裂开,露出内部的结构——一圈圈年轮清晰可见,年轮之间嵌着密密麻麻的数据块。
"它生前是什么?"林小源问。
空地中响起一个声音,来自四面八方:"它是一个 file 对象。open 的时候诞生,close 的时候消亡。它的读写位置停在了 4096——再也不会前进了。"
林小源蹲下来,仔细观察年轮。他注意到,年轮的中心有一个小小的亮点——那是 inode 的核心。即使 file 消失了,inode 依然存在。
"file 和 inode 的区别,"他低声说,"file 是一次打开,inode 是一个文件。一个 inode 可以被打开无数次,但每次打开都是一个独立的 file。"
"你终于明白了,"那个声音说。
林小源站起身,环顾四周。荧光古树组成的森林在他眼前展开,每一棵树都是一个 inode,每一条路都是一个 dentry,守护灵的石碑在远处发出脉动的光。而在树与树之间,无数半透明的光影穿梭往来——那是 file,是进程与文件之间短暂的连接。
他忽然觉得,VFS 这套东西,说穿了就一句话:不管底下是什么文件系统,上面的人感觉都一样。
/*
* VFS 的四大对象:
*
* 1. superblock — 文件系统的元数据
* 超级块,描述整个文件系统
* 挂载时创建,卸载时销毁
*
* 2. inode — 文件的元数据
* 索引节点,描述一个文件
* 包含权限、大小、时间戳等
* 不包含文件名
*
* 3. dentry — 目录项
* 目录项,连接文件名和 inode
* 组成目录树
* 有缓存(dcache)
*
* 4. file — 打开的文件
* 文件对象,描述一次打开
* 包含读写位置、权限等
* 进程关闭后销毁
*
* 关系:
* superblock 包含多个 inode
* dentry 连接文件名和 inode
* file 是进程打开的 inode
*/
printf("=== VFS — 虚拟文件系统 ===\n\n");
printf("VFS 的四大对象:\n\n");
printf("1. superblock(超级块)\n");
printf(" 描述整个文件系统\n");
printf(" 包含块大小、inode 数量、挂载点\n");
printf(" 挂载时创建\n\n");
printf("2. inode(索引节点)\n");
printf(" 描述一个文件\n");
printf(" 包含权限、大小、时间戳\n");
printf(" 不包含文件名\n\n");
printf("3. dentry(目录项)\n");
printf(" 连接文件名和 inode\n");
printf(" 组成目录树\n");
printf(" 有缓存(dcache)\n\n");
printf("4. file(文件对象)\n");
printf(" 描述一次打开操作\n");
printf(" 包含读写位置\n");
printf(" 关闭后销毁\n\n");
printf("--- 对象之间的关系 ---\n");
printf(" superblock\n");
printf(" ├── inode 1 (文件 a)\n");
printf(" ├── inode 2 (文件 b)\n");
printf(" └── inode 3 (目录 /home)\n");
printf(" ├── dentry \"user\" → inode 4\n");
printf(" └── dentry \"file\" → inode 5\n");
printf(" └── file (进程打开)\n\n");
printf("--- VFS 的意义 ---\n");
printf("统一接口: open, read, write, close\n");
printf("透明性: 用户不需要知道底层文件系统\n");
printf("可扩展: 新文件系统只需实现 VFS 接口\n");#include <stdio.h>
/*
* VFS 的四大对象:
*
* 1. superblock — 文件系统的元数据
* 超级块,描述整个文件系统
* 挂载时创建,卸载时销毁
*
* 2. inode — 文件的元数据
* 索引节点,描述一个文件
* 包含权限、大小、时间戳等
* 不包含文件名
*
* 3. dentry — 目录项
* 目录项,连接文件名和 inode
* 组成目录树
* 有缓存(dcache)
*
* 4. file — 打开的文件
* 文件对象,描述一次打开
* 包含读写位置、权限等
* 进程关闭后销毁
*
* 关系:
* superblock 包含多个 inode
* dentry 连接文件名和 inode
* file 是进程打开的 inode
*/
int main() {
printf("=== VFS — 虚拟文件系统 ===\n\n");
printf("VFS 的四大对象:\n\n");
printf("1. superblock(超级块)\n");
printf(" 描述整个文件系统\n");
printf(" 包含块大小、inode 数量、挂载点\n");
printf(" 挂载时创建\n\n");
printf("2. inode(索引节点)\n");
printf(" 描述一个文件\n");
printf(" 包含权限、大小、时间戳\n");
printf(" 不包含文件名\n\n");
printf("3. dentry(目录项)\n");
printf(" 连接文件名和 inode\n");
printf(" 组成目录树\n");
printf(" 有缓存(dcache)\n\n");
printf("4. file(文件对象)\n");
printf(" 描述一次打开操作\n");
printf(" 包含读写位置\n");
printf(" 关闭后销毁\n\n");
printf("--- 对象之间的关系 ---\n");
printf(" superblock\n");
printf(" ├── inode 1 (文件 a)\n");
printf(" ├── inode 2 (文件 b)\n");
printf(" └── inode 3 (目录 /home)\n");
printf(" ├── dentry \"user\" → inode 4\n");
printf(" └── dentry \"file\" → inode 5\n");
printf(" └── file (进程打开)\n\n");
printf("--- VFS 的意义 ---\n");
printf("统一接口: open, read, write, close\n");
printf("透明性: 用户不需要知道底层文件系统\n");
printf("可扩展: 新文件系统只需实现 VFS 接口\n");
return 0;
}道藏笔记
内核启示
VFS 就是文件系统界的"统一接口"。不管你底下是 ext4、btrfs 还是 procfs,上面的人调的都是同一套 open/read/write/close。
四大对象各管一摊事:superblock 管整个文件系统的大局,块大小、inode 数量、挂载标志都在它身上。inode 管单个文件的元数据——权限、大小、时间戳、数据块位置,但偏偏不管文件名,文件名归 dentry 管。dentry 的活儿就是把名字和 inode 串起来,一棵一棵组成目录树。file 最短暂,它是"一次打开",进程关了它就没了,但 inode 还活得好好的。
整个设计思路就三条:统一接口、对上层透明、新文件系统只要实现接口就能接入。就这么简单。
VFS 之试
VFS 古树把不同文件系统统一起来时,文件本体在内核里主要由哪种对象表示?