linux 中 ext2文件系统实现

ext2文件系统结构

图片的svg下载链接(图中关于buffer的部分,上下两部分是重复的,是从不同维度下看的buffer结构)

linux内核本身不提供ext2文件系统的格式化功能,可以参考busybox中对mkfs.ext2的实现(mkfs.ext2.c)格式化过程简述如下:

mkfs_ext2_main():计算每个部分的资源填充super_block填充group_desc_blocks,desc中主要包含三部分数据块的块号bg_block_bitmap,bg_inode_bitmap, bg_inode_table在每个group的bitmap块中标记这些元数据块的位置为已分配(其中第一块还会有super_block)将inode_table空间初始化为0添加. .. lost_found 这三个目录项。

在busybox中块大小与总大小有关,总磁盘大小至少是64k,64k-512m是4096B,512m-4T是8192B(4T以上不考虑了)。一个group中装的block数是一块bitmap块所能承载的数(比如一块4096byte时,能代表的块数是4k*8=32k个块)

从代码流程可以看出ext2系统的布局如下:

没有引导块,super block存了整体信息,group desc存了group信息所在块号(最重要的是三种块的块号:block分配bitmap、inode分配的bitmap、inode 实际存储信息的多个块)。这两部分信息有多个备份。真正的inode和文件内容存在data block上。

ext2中inode分配

inode分配的实现在ext2_new_inode,每次尝试从inode数和block数都占用不多的group分配inode(find_group_orlov),然后从这组的bitmap中找一个合适的位置setbit,它对应了inode_table 中一段实际的磁盘空间。从slab中分配一个包含inode节点的struct ext2_inode_info,将找到的inode号填入inode->ino中,这里不需要访问设备。将inode_table对应位置所在块和inode bitmap 所在块标记为dirty(mark_buffer_dirty),之后就在合适时间等着刷盘就好了(这个链路可以参考linux buffer的回写的触发链路)。

注:存在设备上的inode状态中信息大部分来自于inode 结构。但在内存中会对这个inode结构做一个封装(ext2_inode_info),inode结构存在ext2_inode_info->vfs_inode位置。可以通过EXT2_I(struct inode *inode)函数找到inode对应的ext2_inode_info。

另外当分配一个路径时,每个dir inode有一个对应的block,上面存了ext2_dirent,它是可变长的结构(因为路径的名称可长可短)。当要删除一个路径时,不需要将后面的dirent向前补齐,而是将dirent与前一个dirent合并。所以在分配时也是同理,需要遍历dirent,将合适的dirent的空闲部分切下来作为新的dirent。

ext2中inode的访问

每个inode 的ext2_inode_info中有一个i_data数组,当一个inode文件中分配块数少于12时,可以直接使用i_data的前12个元素作为逻辑块1-12所对应的物理块号。当一个文件块数超过12小于1024(一个block所能装int类型的数量)时,i_data的第13位(index = 12)指向1级列表所在的块,这块中每一项表示对应逻辑块(12-1023)的物理块号。

依此类推,1K-1M之间的逻辑块用i_data[13]所指向的二级块表来索引,1M-1G之间的逻辑块用i_data[14]所指向的三级块表来索引。

当读取一个block时,首先通过inode的块表找到对应逻辑块的物理块号(ext2_block_to_path)。将物理块加载到内存中,封装成一个buffer_head结构,并返回(ext2_get_block)。

buffer_head是设备与buffer之间的一个扭带,它映射了设备上的一片区域(在代码中一个buffer_head对应几个连续的block)。真正的buffer内容存在struct page对应的页空间上(页与struct page的转换关系可以参考:linux中 struct page 与物理地址的关系)

下图中filio是对page的封装,可以理解为一段连续物理内存页的集合,第一页的folio->page上存了页区间的第一个struct page,其后连续的struct folio填满了folio对应的区间。(下图中画的page指向一段页,实际上不是指针关系,而是上面链接提到的,struct page结构地址本身映射了一块内存),这些实际的物理内存组成了一段buffer。区间第一页的folio->private是buffer_head的链表,其上存了这个buffer与块设备上block内容的对应关系,其中buffer_head->b_data指向一个block大小的buffer。(下图中更正:一个buffer_head不一定对应一个块,可以对应几个连续块)

当修改文件中一段内容时,它对应的块的buffer_head会链在inode->private_list所在链表上。等待后期刷盘时遍历。如下图所示:

这里想到一个问题,当父进程fork了一个子进程,这个子进程中通过copy on write 的方式修改了一个文件的内容,会发生什么改变呢?答案是什么也不会发生,因为copy on write是在逻辑地址上做的文章(原理可以参考这篇:理解linux中反向映射与应用),这里的buffer都是物理地址。

ext2中inode块的分配

由于底层访问接口中一次只加载一个block,为了让逻辑上连续的块在物理设备上也尽可能连续,每次为inode分配逻辑块时,会启发性地,一次分配多个连续的块(ext2_get_blocks),我们重点分析下这个启发式过程。

其中每个文件可能有自己的reserve window,但并不保证分配的块一定属于这个文件,有可能在分配reserve window之前其间的物理块早被多个其它文件分配过。reserve window的作用只是说先占着,别的文件分配时会跳过这个区域,从而保证一个文件在分配时物理块尽可能在一起。分配window时的算法保证这个reserve winodw的区域至少有一个空闲块(alloc_new_reservation)。

ext2_get_blocks():// 将块号分解为块表的分量ext2_block_to_path();// 将块表链路上的块都找到,收集到chain中// chain节点Indirect的key=下层物理块号,p=下层逻辑块号所在地址ext2_get_branch();// 选定一个目标块用来分配,原则是尽可能与同一文件分配过的块挨着// 如果上次分配的逻辑块号与物理块号刚好可用,则选定这个块号// 否则在这个块中向左找分配过的物理块号,选定接在它(逻辑上分配过的块的物理块号)后面。// 如果这一逻辑块中前面没有分配过的块,则选承载这级块表的物理块的后面一块。// 如果这是新文件,没有分配过块表,则在inode所在group的1/16*x分配// (x在0到15之间,目的是让新文件分配的物理块尽可能隔开,这样可以使同一文件的物理块尽可能挨着)ext2_find_goal();// 启发式计算一次性分配多少连续块(分配长度),// 这里不要求物理块连续,会在后面将分配长度削减到连续物理块数:// 如果块表的最后一级是中间块,则分配长度为传入的maxblocks块。// 如果maxblocks数量跨过了这个中间块,则分配长度为从逻辑块位置到这个中间块表结束的块数。// 如果块表的最后一级是叶子块,则分配长度为选定逻辑块后没有分配过的连续逻辑块块数。ext2_blks_to_allocate();// 分配块表子链// 传入参数indirect_blks指最后一级块表到分配data块还差几层// blks和goal指上面启发式计算的分配长度和启始分配块ext2_alloc_branch():ext2_alloc_blocks():// 尝试分配至少indirect_blks(新块表项)+1个块,// 其中前面的作为data块,后面的作为新块表块while (分配的连续块还不够用)ext2_new_blocks();// 将buffer_head与这个分配的连续data块绑定(不包含块表块)map_bh(bh_result, bno);// 如果分配的块数超过了最后一级块表的边界,只有一种可能// 就是新分配的块表块接在了新分配的data 块后面。// 则在buffer_head上标记一下,后面刷盘时,除了刷data block外,// 连带要检查一下后面的块,因为它可能是这个data block所在的块表块。// 一起刷盘更能保证原子性(write_boundary_block)set_buffer_boundary(bh_result);ext2_new_blocks():先尝试从选定块的group上的reserve window分配如果不能分配,其它group只取free block大于reserve window 的一半的reserve window分配如果用reserve window分配不成功,尝试不用reserve window分配,还是先goal group,再其它group。实现用了同一个入口函数 ext2_try_to_allocate_with_rsvext2_try_to_allocate_with_rsv():在尝试reserve_window的场景,尝试通过扩张reserve window的方式来满足分配大小,在有指定目标物理块的情况下,尝试让window覆盖目标块如果忽略了目标物理块,则尝试从整个group去分配。扩张有几个原则:1、如果window内已经分配过一半以上,尝试扩张一倍大小。2、如果覆盖了目标块,尝试让reserve window覆盖请求的大小3、扩张后的块内必须至少有一个free的block然后调ext2_try_to_allocate从bitmap中去找一个空闲块。当扩张大小不足以覆盖请求大小时,尝试缩短请求连续块数在不尝试reserve_window的场景,直接调ext2_try_to_allocate找空闲块ext2_try_to_allocate():// 找空闲块有一定算法:// 先从start位置到64 align 的位置结束找free bit.// 如果没找到,尝试找空字节(以8bit为单位找)。// 如果还没找到,才尝试从start位置开始,一个bit一个bit地找。find_next_usable_block();// 当找到空闲bit后,由于上面算法有以8bit为单位找的情况// 因而向前找7个bit,看有没有连续的空闲块可用for (i = 0; i < 7 && grp_goal > start && !ext2_test_bit(grp_goal - 1,bitmap_bh->b_data);i++, grp_goal--);// 返回连续块的开始位置和块数*count = num;return grp_goal - num;

向ext2文件写内容

write的调用到真正操作如下:

write->ksys_write->vfs_write->new_sync_write->call_write_iter->ext2_file_write_iter (ext2 的 op hook)->generic_file_write_iter->__generic_file_write_iter->generic_perform_write->file->f_mapping->a_ops

file的f_mapping就是inode->i_mapping,inode->i_mapping是在从slab拿一个ext2_inode_info时callback hook(init_once)中填充的。而file->f_mapping是在alloc_file时填充的(f_mapping = inode->i_mapping)。

写的过程分为write_begin,copy_page_from_iter_atomic,write_end三步。

第一步ext2_write_begin时,会做两件事,一个是准备buffer,为buffer分配页(会将folio->mapping 置为inode的i_mapping,filemap_add_folio),另一个是准备块,为刷盘区间的逻辑块分配物理块 (get_block)。

第二步迭代传入的用户空间的data,copy到buffer中(copy_page_from_iter_atomic)。

第三步ext2_write_end,标记buffer为dirty(__block_commit_write)。

在写完之后,会调一次sync,来刷盘(generic_file_write_iter->generic_write_sync)。从dirty到刷盘链路可以参考linux buffer的回写的触发链路

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/579585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sheng的学习笔记-【中】【吴恩达课后测验】Course 4 -卷积神经网络 - 第二周测验

课程4_第2周_测验题 目录 第一题 1.在典型的卷积神经网络中&#xff0c;随着网络的深度增加&#xff0c;你能看到的现象是&#xff1f; A. 【  】 n H n_H nH​和 n W n_W nW​增加&#xff0c;同时 n C n_C nC​减少 B. 【  】 n H n_H nH​和 n W n_W nW​减少&#x…

【论文笔记】Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks

论文地址&#xff1a;Run, Dont Walk: Chasing Higher FLOPS for Faster Neural Networks 代码地址&#xff1a;https://github.com/jierunchen/fasternet 该论文主要提出了PConv&#xff0c;通过优化FLOPS提出了快速推理模型FasterNet。 在设计神经网络结构的时候&#xff…

性能优化,让用户体验更加完美(渲染层面)

前言 上一篇我们已经围绕“网络层面”探索页面性能优化的方案&#xff0c;接下来本篇围绕“浏览器渲染层面”继续开展探索。正文开始前&#xff0c;我们思考如下问题&#xff1a; 浏览器渲染页面会经过哪几个关键环节&#xff1f;“渲染层面”的优化从哪几方面着手&#xff1f…

【Redis】一文掌握Redis原理及常见问题

Redis是基于内存数据库&#xff0c;操作效率高&#xff0c;提供丰富的数据结构&#xff08;Redis底层对数据结构还做了优化&#xff09;&#xff0c;可用作数据库&#xff0c;缓存&#xff0c;消息中间件等。如今广泛用于互联网大厂&#xff0c;面试必考点之一&#xff0c;本文…

.NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布

作者&#xff1a; Jon Galloway - Principal Program Manager, .NET Community Team Mehul Harry - Product Marketing Manager, .NET, Azure Marketing 排版&#xff1a;Alan Wang .NET Conf 2023 是有史以来规模最大的 .NET 会议&#xff0c;来自全球各地的演讲者进行了 100 …

设计模式-注册模式

设计模式专栏 模式介绍模式特点应用场景注册模式和单例模式的区别代码示例Java实现注册模式Python实现注册模式 注册模式在spring中的应用 模式介绍 注册模式是一种设计模式&#xff0c;也称为注册树或注册器模式。这种模式将类的实例化和创建分离开来&#xff0c;避免在应用程…

【广州华锐互动】VR科技科普展厅平台:快速、便捷地创建出属于自己的虚拟展馆

随着科技的不断进步&#xff0c;虚拟现实(VR)技术已经在许多领域取得了显著的成果。尤其是在展馆设计领域&#xff0c;VR科技科普展厅平台已经实现了许多令人瞩目的新突破。 VR科技科普展厅平台是广州华锐互动专门为企业和机构提供虚拟展馆设计和制作的在线平台。通过这个平台&…

Git基础学习_p1

文章目录 一、前言二、Git手册学习2.1 Git介绍&前置知识2.2 Git教程2.2.1 导入新项目2.2.2 做更改2.2.3 Git追踪内容而非文件2.2.4 查看项目历史2.2.5 管理分支&#x1f53a;2.2.6 用Git来协同工作2.2.7 查看历史 三、结尾 一、前言 Git相信大部分从事软件工作的人都听说过…

ASP.NET MVC的5种AuthorizationFilter

一、IAuthorizationFilter 所有的AuthorizationFilter实现了接口IAuthorizationFilter。如下面的代码片断所示&#xff0c;IAuthorizationFilter定义了一个OnAuthorization方法用于实现授权的操作。作为该方法的参数filterContext是一个表示授权上下文的AuthorizationContext对…

从计算机内存结构到iOS

一、冯.诺伊曼结构 当前计算机都是冯.诺伊曼结构&#xff08;Von Neumann architecture&#xff09;&#xff0c;是指存储器存放程序的指令以及数据&#xff0c;在程序运行时根据需要提供给CPU使用。 冯.诺伊曼瓶颈 在目前的科技水平之下&#xff0c;CPU与存储器之间的读写速…

挑战与应对:迅软科技探讨IT企业应对数据泄密危机的智慧之路

随着信息技术的快速发展&#xff0c;软件IT行业面临着前所未有的数据安全挑战。黑客攻击、病毒传播、内部泄密等安全威胁层出不穷&#xff0c;给企业的核心资产和运营带来严重威胁。同时&#xff0c;国家对于数据安全的法律法规也日益严格&#xff0c;要求企业必须采取更加有效…

https密钥认证、上传镜像实验

一、第一台主机通过https密钥对认证 1、安装docker服务 &#xff08;1&#xff09;安装环境依赖包 yum -y install yum-utils device-mapper-persistent-data lvm2 &#xff08;2&#xff09;设置阿里云镜像源 yum-config-manager --add-repo http://mirrors.aliyun.com/do…

VLAN简介

在配置交换机或者传输设备时&#xff0c;经常会提到vlan&#xff0c;这个vlan具体是啥呢&#xff1f; VLAN&#xff08;Virtual Local Area Network&#xff09;中文名为“虚拟局域网”。它是一种在物理网络上划分出逻辑网络的方法&#xff0c;将物理上的局域网在逻辑上划分为多…

设计模式——适配器模式(Adapter Pattern)

概述 适配器模式可以将一个类的接口和另一个类的接口匹配起来&#xff0c;而无须修改原来的适配者接口和抽象目标类接口。适配器模式(Adapter Pattern)&#xff1a;将一个接口转换成客户希望的另一个接口&#xff0c;使接口不兼容的那些类可以一起工作&#xff0c;其别名为包装…

分布式下有哪些好用的监控组件?

在之前的内容中&#xff0c;分析了分布式系统下的线上服务监控的常用指标&#xff0c;那么在实际开发中&#xff0c;如何收集各个监控指标呢&#xff1f;线上出现告警之后&#xff0c;又如何快速处理呢&#xff1f;本文我们就来看下这两个问题。 常用监控组件 目前分布式系统…

Node.js版本对比

目录 1. node版本与Npm版本对照表 2. node版本与node-sass版本对照表 3. node-sass与sass-loader版本对照表 1. node版本与Npm版本对照表 以往的版本 | Node.js 下面显示最新的对应内容&#xff0c;如果需要查找历史版本&#xff0c;可以进入上面的页面查询 VersionLTSDateV8np…

鸿蒙实战-库的调用(ArkTS)

整体框架搭建 主页面、本地库组件页面、社区库组件页面三个页面组成&#xff0c;主页面由Navigation作为根组件实现全局标题&#xff0c;由Tabs组件实现本地库和社区库页面的切换。 // MainPage.ets import { Outer } from ../view/OuterComponent; import { Inner } from ..…

【微服务核心】Spring Boot

Spring Boot 文章目录 Spring Boot1. 简介2. 开发步骤3. 配置文件4. 整合 Spring MVC 功能5. 整合 Druid 和 Mybatis6. 使用声明式事务7. AOP整合配置8. SpringBoot项目打包和运行 1. 简介 SpringBoot&#xff0c;开箱即用&#xff0c;设置合理的默认值&#xff0c;同时也可以…

如何让机器人具备实时、多模态的触觉感知能力?

人类能够直观地感知和理解复杂的触觉信息&#xff0c;是因为分布在指尖皮肤的皮肤感受器同时接收到不同的触觉刺激&#xff0c;并将触觉信号立即传输到大脑。尽管许多研究小组试图模仿人类皮肤的结构和功能&#xff0c;但在一个系统内实现类似人类的触觉感知过程仍然是一个挑战…

【go语言】CSP并发机制与Actor模型

一、多线程共享内存 1. 概念 多线程共享内存模型是一种并发编程模型&#xff0c;其中多个线程在同一个进程的地址空间中共享相同的内存区域。这种模型允许多个线程并发地读取和写入相同的数据结构&#xff0c;但也引入了一些潜在的问题&#xff0c;其中最常见的问题之一就是…