Linux内核-文件系统-超级块操作
文件系统的超级块操作主要是在super.c这个源文件中这个文件的作用主要有对设备的超级块进行操作获取读取释放因为超级块是设备文件系统的映射所以超级块的操作关系到设备文件系统的操作包含文件系统的加载卸载挂载等根文件系统的加载get_super//// 取指定设备的超级块。返回该超级块结构指针 struct super_block * get_super(int dev) { struct super_block * s; if (!dev) return NULL; s 0super_block; // s 指向超级块数组开始处。搜索整个超级块数组寻找指定设备的超级块 while (s NR_SUPERsuper_block) // 如果当前搜索项是指定设备的超级块则首先等待该超级块解锁若已经被其它进程上锁的话。 // 在等待期间该超级块有可能被其它设备使用因此此时需再判断一次是否是指定设备的超级块 // 如果是则返回该超级块的指针。否则就重新对超级块数组再搜索一遍因此 s 重又指向超级块数组 // 开始处 if (s-s_dev dev) { wait_on_super(s); if (s-s_dev dev) return s; s 0super_block; } else s; return NULL; }这个函数和前面几次文章的函数都差不多获取指定设备的超级块put_super//// 释放指定设备的超级块 void put_super(int dev) { struct super_block * sb; struct m_inode * inode; int i; if (dev ROOT_DEV) { printk(root diskette changed: prepare for armageddon\n\r); return; } if (!(sb get_super(dev))) return; if (sb-s_imount) { printk(Mounted disk changed - tssk, tssk\n\r); return; } lock_super(sb); sb-s_dev 0; for(i0;iI_MAP_SLOTS;i) brelse(sb-s_imap[i]); for(i0;iZ_MAP_SLOTS;i) brelse(sb-s_zmap[i]); free_super(sb); return; }put_super用于释放掉指定的超级块在这里需要注意如果设备是根目录那不能被释放以及s_imount如果被挂载到了某个inode也是不能被释放的对于释放操作主要是将s_dev置0让系统不能找到这个超级块然后将inode位图和逻辑块位图清空读取超级块到内存read_super//// 从设备上读取超级块到内存中。 // 如果该设备的超级块已经在高速缓冲中并且有效则直接返回该超级块的指针 static struct super_block * read_super(int dev)查看软驱是否有更改check_disk_change(dev);check_disk_change(dev);这行代码的核心作用是检测指定设备主要是软盘上的介质是否被更换过。如果被更换了就使内核中该设备相关的所有旧缓存数据失效以确保接下来读取到的是新插入介质上的正确超级块。由于这段代码出自 Linux 0.11 内核那个时代主要依赖软盘作为移动存储设备且系统不支持热插拔通知机制所以这个函数是保证数据一致性的关键“防御性”检查。他的底层机制大致如下void check_disk_change(int dev) { int i; if (MAJOR(dev) ! 2) // 只对软盘设备主设备号 2进行检查 return; if (!floppy_change(dev)) // 调用软盘驱动检测磁盘是否真的被抽换过 return; // 如果检测到介质已更换立即执行清理操作 // 1. 从超级块数组中清除该设备的旧超级块 for (i 0; i NR_SUPER; i) if (super_block[i].s_dev dev) put_super(dev); // 2. 使内存中该设备的所有 i 节点inode失效 invalidate_inodes(dev); // 3. 使高速缓冲区buffer cache中该设备的所有数据块失效 invalidate_buffers(dev); }如果在高速缓冲直接拿取if (s get_super(dev)) // 如果该设备的超级块已经在高速缓冲中则直接返回 return s; for (s 0super_block ;; s) { // 否则首先在超级块数组中找出一个空项(也即其 s_dev0 的项)。如果数组已经占满则返回空指针 if (s NR_SUPERsuper_block) return NULL; if (!s-s_dev) // 当前dev为空 break; }如果get_super能够拿到那就说明已经在高速缓冲中了直接返回如果没有的话就需要在超级块数组中找到一个空闲的项存放这个设备的超级块设置动态配置项// 找到超级块空项后就将该超级块用于指定设备对该超级块进行部分初始化 //这里设置的都是一些超级块内存中的一些动态配置项 s-s_dev dev; s-s_isup NULL; s-s_imount NULL; s-s_time 0; s-s_rd_only 0; s-s_dirt 0; lock_super(s);在得到一个空闲的超级块数组项后就去初始化一些超级块存储在内存中的动态配置项读取超级块固定配置项到高速缓冲区if (!(bh bread(dev,1))) { // 从设备上读取超级块信息到 bh 指向的缓冲区中 s-s_dev0; free_super(s); return NULL; } // 将设备上读取的超级块信息复制到内存超级块结构中。并释放存放读取信息的高速缓冲块 *((struct d_super_block *) s) *((struct d_super_block *) bh-b_data); brelse(bh);超级块结构体有一部分是存储在磁盘上的固定配置项包含了节点数逻辑块数位图占据逻辑块数等等使用bread读取这部分内容到高速缓冲区这里的1表示的就是超级块的逻辑块号如果成功拿取到超级块的固定配置项就赋值拷贝到内存s这里也是直接结构体赋值是不规范的最后释放掉使用的这块高速缓冲区根据固定配置项设置位图信息//s_magic是文件系统ID号如果不支持则释放掉这个超级块 if (s-s_magic ! SUPER_MAGIC) { // 说明设备上不是正确的文件系统 s-s_dev 0; free_super(s); return NULL; } // 下面开始读取设备上 i 节点位图和逻辑块位图数据 for (i0;iI_MAP_SLOTS;i) // 初始化 s-s_imap[i] NULL; for (i0;iZ_MAP_SLOTS;i) s-s_zmap[i] NULL; block2; //指向i节点位图 //根据得到的超级块信息分配该设备的i节点位图 for (i0 ; i s-s_imap_blocks ; i) // 读取 if (s-s_imap[i]bread(dev,block)) // 表示inode block; else break; //根据得到的超级块信息分配该设备的逻辑块位图 for (i0 ; i s-s_zmap_blocks ; i) if (s-s_zmap[i]bread(dev,block)) // 表示逻辑块 block; else break; //出错处理还原一切 if (block ! 2s-s_imap_blockss-s_zmap_blocks) { for(i0;iI_MAP_SLOTS;i) brelse(s-s_imap[i]); for(i0;iZ_MAP_SLOTS;i) brelse(s-s_zmap[i]); s-s_dev0; free_super(s); return NULL; }最上面是去判断是不是支持的文件系统不是的话就直接返回NULL然后接下来将设备上的每块i节点位图和逻辑块位图置空等待接下来的分配让block2; //指向i节点位图然后从第一块i节点位图到最后一块逻辑块位图依次分配高速缓冲块如果block ! 2s-s_imap_blockss-s_zmap_blocks就代表出现问题直接将一切还原将位图第一位置1// 一切成功。对于申请空闲 i 节点的函数来讲如果设备上所有的 i 节点已经全被使用则查找 // 函数会返回 0 值。因此 0 号 i 节点是不能用的所以这里将位图中的最低位设置为 1以防止文件 // 系统分配 0 号 i 节点。同样的道理也将逻辑块位图的最低位设置为 1 s-s_imap[0]-b_data[0] | 1; s-s_zmap[0]-b_data[0] | 1; // 解锁该超级块并返回超级块指针 free_super(s); return s;位图的第一个bit位是固定不使用的所以强制让他们一直被占用挂载文件系统sys_mount/** * brief 安装文件系统 * * param dev_name 设备文件名 * param dir_name 安装到的目录名 * param rw_flag 被安装文件的读写标志 * return int 0-成功other-错误号 */ int sys_mount(char * dev_name, char * dir_name, int rw_flag)这个函数是mount的系统调用将dev_name的设备挂载到dir_name目录上获取设备和目录i节点struct m_inode * dev_i, * dir_i; struct super_block * sb; int dev; if (!(dev_inamei(dev_name))) // 首先根据设备文件名找到对应的 i 节点并取其中的设备号 return -ENOENT; dev dev_i-i_zone[0]; // 通过i节点获取设备号 if (!S_ISBLK(dev_i-i_mode)) { iput(dev_i); return -EPERM; } iput(dev_i); // 释放刚申请的 i 节点 dev_i if (!(dir_inamei(dir_name))) return -ENOENT; if (dir_i-i_count ! 1 || dir_i-i_num ROOT_INO) { iput(dir_i); return -EBUSY; } if (!S_ISDIR(dir_i-i_mode)) { iput(dir_i); return -EPERM; }1.首先是通过namei函数根据设备文件名和目录名去获取得到设备和目录对应的inode在获取得到设备inode后要去判断它是不是一个块设备S_ISBLK并且去获取得到设备号,块设备inode中的i_zone第一个直接块号存储的设备号dev dev_i-i_zone[0];在完成判断之后就可以释放掉设备的inode节点了,所以才会看到不管是不是块设备都有iput(dev_i);2.然后也会去查看当前要被挂载的文件目录是否没有被其他占用dir_i-i_count并且不是根目录ROOT_INO,以及是否是目录S_ISDIR获取设备超级块if (!(sbread_super(dev))) { // 通过设备号读取设备的超级块 iput(dir_i); return -EBUSY; } if (sb-s_imount) { iput(dir_i); return -EBUSY; } if (dir_i-i_mount) { iput(dir_i); return -EPERM; }这里根据设备号去获取得到设备超级块因为超级块存储着设备文件系统的相关信息通过s_imount i_mount去查看设备和目录是否已经被挂载,如果已经被挂载就退出设置挂载信息sb-s_imountdir_i; // 被安装设备挂载到目录设备节点 dir_i-i_mount1; // 已挂载已修改 dir_i-i_dirt1; /* NOTE! we dont iput(dir_i) */ return 0; /* we do that in umount 在umount 中释放 */在最后将设备的挂载节点指向目录节点,然后将它们各自的挂载标志位置1卸载文件系统sys_umount卸载文件系统sys_umount这里跟上面的挂载操作的思路类似这里是遍历寻找然后间朝状态位最后置位对应信息位,在此不赘述/** * brief 卸载文件系统调用函数 * * param dev_name 设备文件名 * return int 0-成功other-错误号 */ int sys_umount(char * dev_name) { struct m_inode * inode; struct super_block * sb; int dev; if (!(inodenamei(dev_name))) return -ENOENT; dev inode-i_zone[0]; if (!S_ISBLK(inode-i_mode)) { iput(inode); return -ENOTBLK; } iput(inode); if (devROOT_DEV) return -EBUSY; if (!(sbget_super(dev)) || !(sb-s_imount)) return -ENOENT; if (!sb-s_imount-i_mount) printk(Mounted inode has i_mount0\n); for (inodeinode_table0 ; inodeinode_tableNR_INODE ; inode) if (inode-i_devdev inode-i_count) return -EBUSY; sb-s_imount-i_mount0; iput(sb-s_imount); sb-s_imount NULL; iput(sb-s_isup); sb-s_isup NULL; put_super(dev); sync_dev(dev); return 0; }挂载根文件系统mount_root// 加载根文件系统 统开机初始化设置时(sys_setup())调用的 void mount_root(void)根文件系统挂载是在开机时的sys_setup调用的sys_setup函数内部主要是获取BIOS信息硬盘信息加载主存最后就是调用mount_root挂载初始化文件表和超级块数组int i,free; struct super_block * p; struct m_inode * mi; if (32 ! sizeof (struct d_inode)) panic(bad i-node size); for(i0;iNR_FILE;i) // 初始化文件表数组共 64 项也即系统同时只能打开 64 个文件将所有文件结构中的引用计数设置为0 file_table[i].f_count0; if (MAJOR(ROOT_DEV) 2) { // 如果根文件系统所在设备是软盘的话就提示“插入根文件系统盘并按回车键”并等待按键 printk(Insert root floppy and press ENTER); wait_for_keypress(); } for(p super_block[0] ; p super_block[NR_SUPER] ; p) { // 初始化超级块数组共 8 项 p-s_dev 0; p-s_lock 0; p-s_wait NULL; }在函数开始首先判断d_inode大小是否是32,如果不是就报错然后去初始化文件表数组将每一项的文件描述符都清零// 文件表数组 系统同时只能打开64个文件 struct file file_table[NR_FILE];并且对于软盘设备需要用户敲击键盘启动最后就是初始化超级块数组设置s_dev,s_lock,s_wait为0获取根设备超级块和inodeif (!(pread_super(ROOT_DEV))) // 读根设备上超级块 panic(Unable to mount root); if (!(miiget(ROOT_DEV,ROOT_INO))) //从设备上读取文件系统的根 i 节点 panic(Unable to read root i-node); mi-i_count 3 ; /* NOTE! it is logically used 4 times, not 1 */ p-s_isup p-s_imount mi; // 设置i节点挂载 current-pwd mi; // 设置当前工作目录和根目录i节点 current-root mi;接下来就是获取设备的超级块和inode这里的i_count3后实际数值是4,那么是哪4个指针在使用它呢mount_root()的后续代码中这个mi根目录 inode被赋值给了以下位置p-s_isup p-s_imount mi; // ① 超级块的 s_isup② 超级块的 s_imount current-pwd mi; // ③ 当前进程的工作目录 current-root mi; // ④ 当前进程的根目录s_isup文件系统超级块中指向“根目录”的指针用于路径解析的起点。s_imount超级块中指向“被挂载的 inode”的指针虽然根文件系统挂载在自身但这个指针必须有效。current-pwd当前进程的当前工作目录。current-root当前进程的根目录用于chroot环境根文件系统下就是根目录本身。iget(ROOT_DEV, ROOT_INO)在返回 inode 指针时会将该 inode 的i_count设置为1表示“有人正在使用这个 inode 结构体”。统计设备空闲块数和节点数// 统计该设备上空闲块数。 // 首先令 i 等于超级块中的设备逻辑块总数 // 然后根据位图统计出空闲总块 // 接着统计设备上空闲i节点数 free0; ip-s_nzones; while (-- i 0) if (!set_bit(i8191,p-s_zmap[i13]-b_data)) free; printk(%d/%d free blocks\n\r,free,p-s_nzones); free0; ip-s_ninodes1; while (-- i 0) if (!set_bit(i8191,p-s_imap[i13]-b_data)) free; printk(%d/%d free inodes\n\r,free,p-s_ninodes);

相关新闻

局部莫兰指数(LISA)原理、计算与可视化:空间热点探测全解析

局部莫兰指数(LISA)原理、计算与可视化:空间热点探测全解析

1. 项目概述:从全局到局部,洞察空间异质性的关键一步在空间数据分析领域,我们常常需要回答一个核心问题:某个区域的现象,是否与其周边区域存在某种关联?全局莫兰指数(Global Moran‘s I&#xf…

2026/10/24 16:22:13 阅读更多 →
2026年8月更新:ChatGPT、Codex、Pro、Plus背后的Policy as Code——为什么Agent规则不能只写在提示词里?

2026年8月更新:ChatGPT、Codex、Pro、Plus背后的Policy as Code——为什么Agent规则不能只写在提示词里?

过去,团队管理开发规范,主要依靠代码审查、文档和人的经验。进入Agent开发阶段后,规则直接影响执行:Agent能否安装依赖、访问网络、修改目录、继续重试和完成验证,都会决定自动化是安全交付,还是快速扩散错…

2026/10/22 5:25:01 阅读更多 →
CentOS 7.9 上搭建 Valheim 专用服务器:从零部署到稳定运维

CentOS 7.9 上搭建 Valheim 专用服务器:从零部署到稳定运维

1. 从零开始的英灵神殿服务器:为什么选择自己搭建?如果你最近被朋友安利了《英灵神殿》(Valheim),或者已经沉迷于这个维京风格的生存建造游戏,那你大概率会遇到一个共同的烦恼:联机。游戏自带的…

2026/10/21 1:36:24 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/25 11:19:54 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/24 17:47:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/24 18:34:36 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/25 10:02:42 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/25 3:12:30 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/24 21:09:59 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 10:02:43 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/24 11:09:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 7:47:32 阅读更多 →