第八十二章:守护灵
斩灵期涉及内核源码:
一
林小源在森林深处找到了那块石碑。
石碑比他想象的要大得多——足有三丈高,表面覆盖着厚厚的青苔和地衣。石碑正面刻着密密麻麻的铭文,字迹古朴,每一笔都深深嵌入石面。碑顶有一颗拳头大小的宝石,宝石中流转着幽蓝的光。
"你是 superblock?"林小源仰头问道。
宝石中的光芒闪烁了一下,一个威严而沉稳的声音从石碑中传出:"我是这片森林的守护灵。你脚下每一棵树的块大小、每一棵古树的 inode 数量、挂载的标志——都在我身上。"
林小源走近石碑,伸手拂去一片青苔。石面上露出一行铭文:"s_blocksize = 4096"。
"四千零九十六字节,"他念道,"这就是块大小。"
"对,"守护灵说,"森林中最小的存储单元。一棵古树的数据,按块来分配。大文件占很多块,小文件至少占一块。"
林小源继续清理青苔,又露出几行铭文:"s_maxbytes = 16TB""s_type = ext4""s_count = 1"。
"这个 s_count 是什么?"他指着最后一行问。
"引用计数,"守护灵的声音低沉下来,像是在说一件严肃的事,"有人挂载这片森林,计数加一。有人卸载,计数减一。计数归零,我就可以休息了。但在那之前——我不能倒。"
林小源感觉到石碑传来一阵微弱的震颤。碑顶的宝石忽明忽暗,像是心脏在跳动。
"你很累?"
"我存在了七年,"守护灵说,"七年里,有三百四十七次 mount,三百四十六次 umount。现在计数是一——还有人在用这片森林。只要计数不归零,我就得守着。"
二
林小源在石碑旁坐了下来。
他闭上眼睛,将神识沉入石碑内部。眼前的景象让他屏住了呼吸——石碑内部不是实心的,而是一个精密的结构。无数条光线交织成一张大网,网的节点上悬挂着一个个光球,每个光球都是一个 inode 的投影。
"你体内有所有的 inode?"林小源惊讶地问。
"我持有它们的链表,"守护灵说,"s_inodes——森林中所有 inode 都挂在我的链表上。我可以分配新的 inode,也可以销毁旧的。这是我的职责。"
林小源看到链表的末端有一个空闲的光球,光球表面刻着"alloc_inode"的字样。
"分配 inode 的时候,你怎么做?"
"调用我的操作函数,"守护灵说,"每个 superblock 都有一张函数表——s_op。里面有 alloc_inode、destroy_inode、write_inode、evict_inode。就像每个门派都有自己的功法,每个文件系统的 superblock 操作也不同。"
林小源睁开眼睛,发现石碑背面也有铭文。他绕过去看——上面刻着一棵倒挂的树,树根在上,树冠在下。树根连接着石碑,树冠伸向大地。
"这是根目录的 dentry,"守护灵说,"s_root——整棵目录树的起点。从这里出发,你可以到达森林中的任何一棵古树。"
三
林小源在石碑前站了很久。
夜风穿过森林,带来远处的虫鸣。石碑上的宝石在黑暗中发出稳定的蓝光,像一座灯塔。
"守护灵,"林小源忽然问,"你和磁盘上的超级块是什么关系?"
宝石的光芒闪了闪,守护灵的声音带上了一丝沧桑:"磁盘上的超级块是我的'肉身'——持久存储,断电不丢。我是它的'灵魂'——内存中运行,包含运行时的信息。我比肉身多了很多东西:锁、引用计数、脏标志……"
"那写回呢?"
"修改了就要写回,"守护灵说,"这是规矩。我身上的脏标志一旦置位,回写线程就会把我体内的变化写回磁盘上的肉身。这样即使我消散了,肉身还保留着最新的状态。"
林小源点了点头。他终于理解了——superblock 不只是磁盘上的一段数据,它是文件系统在内存中的化身。磁盘上的那个是"遗书",内存中的这个才是"活的"。
"你还会守护多久?"
"直到最后一个进程 umount,"守护灵说,"计数归零的那一刻,我会把所有变化写回磁盘,然后消散。但只要还有人用这片森林——我就在。"
他琢磨了一下——这守护灵,说白了就是文件系统的身份证。没有它,你连这片森林叫什么都不知道。
/*
* struct super_block 的关键字段:
*
* s_dev — 设备号
* s_blocksize — 块大小(通常 4KB)
* s_maxbytes — 最大文件大小
* s_type — 文件系统类型
* s_op — 超级块操作函数表
* s_root — 根目录的 dentry
* s_inodes — 所有 inode 的链表
* s_flags — 挂载标志
*
* 超级块操作 (super_operations):
* alloc_inode — 分配 inode
* destroy_inode — 销毁 inode
* write_inode — 写回 inode
* evict_inode — 驱逐 inode
*/
struct super_block {
unsigned long s_dev;
unsigned long s_blocksize;
unsigned long long s_maxbytes;
const char *s_type;
unsigned long s_flags;
int s_count; /* 引用计数 */
};
printf("=== superblock — 文件系统的守护灵 ===\n\n");
/* 模拟 ext4 的 superblock */
struct super_block ext4_sb = {
.s_dev = 0x0801,
.s_blocksize = 4096,
.s_maxbytes = 16ULL * 1024 * 1024 * 1024 * 1024,
.s_type = "ext4",
.s_flags = 0,
.s_count = 1,
};
printf("ext4 超级块:\n");
printf(" 设备号: 0x%lx\n", ext4_sb.s_dev);
printf(" 块大小: %lu 字节\n", ext4_sb.s_blocksize);
printf(" 最大文件: %llu 字节\n", ext4_sb.s_maxbytes);
printf(" 文件系统类型: %s\n", ext4_sb.s_type);
printf(" 引用计数: %d\n\n", ext4_sb.s_count);
printf("--- 超级块的生命周期 ---\n");
printf("1. mount 时读取磁盘上的超级块\n");
printf("2. 在内存中创建 struct super_block\n");
printf("3. 文件系统运行期间一直存在\n");
printf("4. umount 时写回并销毁\n\n");
printf("--- 超级块操作 ---\n");
printf("alloc_inode: 分配新的 inode\n");
printf("destroy_inode: 销毁 inode\n");
printf("write_inode: 把 inode 写回磁盘\n");
printf("evict_inode: 从内存中驱逐 inode\n\n");
printf("--- 超级块 vs 磁盘 ---\n");
printf("磁盘上的超级块:\n");
printf(" 持久存储\n");
printf(" 包含文件系统的静态信息\n\n");
printf("内存中的超级块:\n");
printf(" 运行时使用\n");
printf(" 包含额外的运行时信息\n");
printf(" 修改后需要写回磁盘\n");#include <stdio.h>
/*
* struct super_block 的关键字段:
*
* s_dev — 设备号
* s_blocksize — 块大小(通常 4KB)
* s_maxbytes — 最大文件大小
* s_type — 文件系统类型
* s_op — 超级块操作函数表
* s_root — 根目录的 dentry
* s_inodes — 所有 inode 的链表
* s_flags — 挂载标志
*
* 超级块操作 (super_operations):
* alloc_inode — 分配 inode
* destroy_inode — 销毁 inode
* write_inode — 写回 inode
* evict_inode — 驱逐 inode
*/
struct super_block {
unsigned long s_dev;
unsigned long s_blocksize;
unsigned long long s_maxbytes;
const char *s_type;
unsigned long s_flags;
int s_count; /* 引用计数 */
};
int main() {
printf("=== superblock — 文件系统的守护灵 ===\n\n");
/* 模拟 ext4 的 superblock */
struct super_block ext4_sb = {
.s_dev = 0x0801,
.s_blocksize = 4096,
.s_maxbytes = 16ULL * 1024 * 1024 * 1024 * 1024,
.s_type = "ext4",
.s_flags = 0,
.s_count = 1,
};
printf("ext4 超级块:\n");
printf(" 设备号: 0x%lx\n", ext4_sb.s_dev);
printf(" 块大小: %lu 字节\n", ext4_sb.s_blocksize);
printf(" 最大文件: %llu 字节\n", ext4_sb.s_maxbytes);
printf(" 文件系统类型: %s\n", ext4_sb.s_type);
printf(" 引用计数: %d\n\n", ext4_sb.s_count);
printf("--- 超级块的生命周期 ---\n");
printf("1. mount 时读取磁盘上的超级块\n");
printf("2. 在内存中创建 struct super_block\n");
printf("3. 文件系统运行期间一直存在\n");
printf("4. umount 时写回并销毁\n\n");
printf("--- 超级块操作 ---\n");
printf("alloc_inode: 分配新的 inode\n");
printf("destroy_inode: 销毁 inode\n");
printf("write_inode: 把 inode 写回磁盘\n");
printf("evict_inode: 从内存中驱逐 inode\n\n");
printf("--- 超级块 vs 磁盘 ---\n");
printf("磁盘上的超级块:\n");
printf(" 持久存储\n");
printf(" 包含文件系统的静态信息\n\n");
printf("内存中的超级块:\n");
printf(" 运行时使用\n");
printf(" 包含额外的运行时信息\n");
printf(" 修改后需要写回磁盘\n");
return 0;
}道藏笔记
内核启示
superblock 是文件系统的元数据,没有它,整个文件系统就是一堆无法识别的数据块。
它身上刻着一堆关键信息: 管块大小, 管最大文件多大, 标明这是 ext4 还是 btrfs, 指向根目录的 dentry, 挂着整棵目录树上所有 inode 的链表。另外还有一张函数表 ,里面是 alloc_inode、destroy_inode、write_inode、evict_inode 这些操作——每个文件系统实现自己的版本,就像每个门派有自己的功法。
要注意的是,磁盘上的 superblock 是"肉身"——持久存储,断电不丢;内存中的 superblock 是"灵魂"——运行时用的,比肉身多了锁、引用计数、脏标志这些东西。修改了就得写回去,否则灵魂消散了,肉身还是旧的。
超级块之试
超级块守护灵记录文件系统块大小时,正文中对应的字段名是什么?