当我们谈到文件系统的时候,最重要的点在于:文件的内容与属性是如何存储在磁盘中的?以及操作系统是如何精准定位到这些文件内容的?在谈及文件的内核前,我们先来了解一下储存文件的硬件-----硬盘

一.理解硬件

首先我们来看一下传统磁盘的基础结构

磁盘是由磁头,主轴,磁盘,磁头臂,马达组成的。类似于针一样的就是磁头,它可以左右移动;中间的圆盘就是主轴 ,会带动磁盘高速旋转;空心圆就是磁盘,里面存储了数据;磁头臂控制磁盘的移动。

基于这种组成结构,那么磁盘是如何运转的呢?

首先计算机只能对二进制进行识别,所以说磁盘当中的信息存储就是用无数的0和1构成的。磁盘在高速旋转中,磁头与磁盘之间是有间距的并不是紧贴在磁盘上。磁盘通过顺时针和逆时针的旋转区别出0和1的信息,磁头通过磁场感知将磁信号转化成电信号。电信号再被传输到内存当中。

那么磁盘是如何存储信息的,下面我们来看一下盘片的区域划分的。

在硬盘中分为磁头,磁道,扇区这三块。如图为三片六面,其中每一面都存在着一个磁头,每个磁头对应着相应的盘面,盘面以圆盘为中心向外存在着一圈一圈的称为磁道, 每一圈磁道之间都有间隙,间隙与间隙之间的范围称为扇区。

在了解了上述三个概念之后,我们还要引入柱体这一概念。

 柱体是根据磁道来确定的,硬盘通常是由多片磁盘组成,有2片4片,每片都存在着上下磁头,其中所有的磁头都有机械轴串起来,所有磁头与圆盘圆心的距离都是相同,这也就意味着,当1号磁头停留在1号磁盘的1号磁道上,那么2号磁头会在2号盘面上停留在1号磁道上,以此类推。该硬盘中这些磁道立体起来就变成了一个柱体。

那么我们应当如何定位硬盘中的任意位置?

我们先从物理结构来理解!

这里我们引入了“CHS定址法”。

通过上文讲的柱体概念我们可以很轻松的理解。首先我们要先确定柱体的半径大小,也就是磁道的编号,对应的(Cylinder)。接着确定柱体高度,也就是磁头的编号,对应的(Header)。最后确认扇区的位置,对应的(Sector)。我们就完成了对硬盘的定位。类似于数学中的三维坐标,这里我们只是相应的更换了坐标系用“CHS”来进行定位。一个扇区大小通常为512字节,我们可以通过一个扇区大小来计算出硬盘的存储量大小。

磁盘容量 = 磁头数 * 磁道数 * 每条磁道扇区数 * 每扇区字节数

接下来我们从逻辑结构来理解!

我们用磁带来类比一下。

 磁带分为左右两边,左边输入右边接收中间识别信息。我们将磁带展开就是一条线性结构。

类比于硬盘,我们将同一条磁道上的每一个扇区划分为一个单元格,我们对其展开,一条磁道上就是一条线性结构,我们可以将其类比于一个一维数组;而在同一个柱面上存在着许多类似的磁道,同样的进行展开,就组成了一个面,我们将其类比于一个二维数组;整个硬盘由许多此类的柱面形成,我们想象成柱面卷成了一个更大的实心柱体,我们将其类比于一个三维数组。所以说,我们可以将硬盘简单的理解成一个三维数组。

 一维磁道展开

 二维柱面展开

三维 柱面合成

 

 我们将硬盘类比于三维数组,通过数组下标我们便可以访问到数组任意位置的信息,将下标信息转化成CHS下标进行管理。

由于每个扇区大小较小,如果系统对一个扇区一个扇区进行管理对于操作系统来说消耗太频繁。通常我们将八个扇区作为一组也就是4KB大小,我们将这八个扇区称为“块”。操作系统通过块号来定位文件,从而屏蔽底层对于扇区的复杂定位,使得我们可以快速找到相应的扇区。

这里我们引入了“逻辑区块地址” 也就是“LBA”(Logical Block Address)

我们将硬盘划分为一个个块,定义为区块1,区块2等等。意味着我们只要知道起始地址,磁盘的总大小,我们就可以定位到磁盘每个单位的下标,再通过CHS进行计算就可获得对应的地址。因此,LBA寻址是对CHS寻址的逻辑抽象,LBA好似一个个门牌号而CHS更像是一间间确定的房间号,前者是对后者的简化和升级。LBA寻址后也需要再转换成CHS寻址确定位置。

二. 文件系统

我们整个磁盘有不同的大小之分,800GB,1TB等等,若操作系统在每次操作时都对整个磁盘进行扫描查找,必定会耗费大量的时间。结合我们的生活实际,通常我们会将一个大盘分为几个小盘,C盘,D盘,F盘,这里就是采用了分治的思想。我们将各个区域分而治之,可以简化管理提高我们操作的灵活性。

在类UNIX文件系统(如ext系列),完成分区之后,操作系统还会对每一个分区进行分组(Block Group)处理,将一个分区进行多个分组。每个组中会包含超级块(Super Block),GDT(Group Description Table),块位图(Block Bitmap),inode位图(inode Bitmap),inode表,数据区。

我们只要能弄清楚一个组中是如何工作的,根据分治的思想我们就能理解整个文件系统是如何运转的。下面我们来理解一下上面的几个概念。

超级块:这是一个存储整个文件结构信息的块,类似于整个文件的地图指南。主要记录Block和inode的总量,使用情况,大小,最近一次挂载的时间,最近一次写入数据的时间。若超级块被破坏了,可以说整个文件系统结构就被破坏了。所以说,通常在每一个组中都会有一份超级块进行备份,以免损坏后文件结构破坏。

GDT:块组描述符,描述块组的属性信息。整个分区有多少个块组就对应有多少个块组描述符,记录inode Table,Data Block的起始位置,空闲inode和Data数量,GDT在每个块组开头都有一份拷贝。

块位图:应用了位图的方式用1表示该数据块被占用,0表示未被占用。

inode位图:表示inode是否空闲可用,对inode是否占用进行映射。

inode表:存放文件属性的表。

数据区:存放文件内容的区域。

我们知道 文件 = 内容 + 属性,文件内容被存放在数据区中,文件属性被存放在inode表中,一个文件可以没有内容,但是一定会有属性。这里的inode相当于一个结构体,存储着文件属性,以及指向该文件数据区的指针。inode结构体中存在一个inode编号,使得操作系统可以定位区分不同的文件,inode结构体内又存在着数据区的指针,可以找到文件的内容,所以说,我们只要知道了inode编号我们就能得到文件的内容+属性。

inode结构体:文件大小,文件所有者和所属ID,文件类型权限,时间戳,指向文件数据块的索引指针(双重,三重指针)

 在知道inode号,我们如何对文件进行增删查改?

增:遍历inode位图找到空闲的空间,分配数据块,更新inode信息。

删:通过inode号遍历到inode,将inode位图从0变为1。

查:用inode号找到inode表,判断用户权限大小,通过inode指针获取数据块内容。

改:定位inode并检查权限,修改数据块内容,最后更新inode表内容。

我们知道Linux下一切皆文件,同样的目录也是文件。inode里面不存文件名,文件名是存储在目录里的,我们在目录下寻找文件,就是因为文件名与inode形成了一层映射关系。因此在同一个目录下不能有两个相同的文件名,因为不能有两个相同的inode出现,所以说 文件名就是与inode对应。

对于一个文件的访问权限,实际就是对于inode的访问权限 ,inode会将权限信息存储起来,即使对文件名进行删除也不会影响到inode的访问权限。

目录名也是一个inode,它也有对应的数据块。我们在目录下寻找文件就是先从根目录开始,向下寻找下一个目录的inode,在目录的inode中再向下寻找,以此类推。我们将这样的过程称为路径解析,为了提高运行的效率。Linux内核中有dentry(directory entry)缓存,可以缓存近期解析的路径信息,这样可以减少操作系统的开销。

下面我们通过一个完整详细的流程来描述用户程序中fwrite()写入数据时,底层发生了什么,以及新文件的更新流程。

#include <stdio.h>
#include <stdlib.h>int main()
{FILE* fp = fopen("newfile.txt", "w");if (fp == NULL){perror("fopen");return 1;}const char* data = "Hello,fwrite!\n";size_t wrote = fwrite(data, 1, sizeof(data) - 1, fp);if (wrote < sizeof(data)){perror("data");return 1;}fclose(fp);return 0;
}

一. fopen阶段

首先fopen()是C标准库函数,不直接完成创建,它会调用系统调用(open())完成底层操作。fopen()根据"w"权限,会调用open(“newfile.txt”,O_WRONLY|O_CREAT|O_TRUNC,0666)。若文件不存在,传入的O_CREAT就会创建一个新文件。

接着内核接收到open()的调用,会进行路径解析。从根目录开始查找文件所在目录,通过dentry和inode表来找到该文件的inode。若该文件不存在,就要重新进行资源分配。

若文件不存在,那么就要为这一新创建的文件进行inode和Block的分配。先查找inode位图,找到空闲的inode号,给新文件使用。在inode表中初始化该文件的属性,此时不需要立即分配数据块,数据块将在后续写入后进行分配。

此时内核需要对当前文件的目录添加该文件的inode号,使目录的数据块对于inode号产生一个映射。

最后返回一个FILE*对象(struct file),并在进程中记录。open()返回文件描述符fd,fopen()接受到fd后为其分配FILE*结构。

二. fwrite()阶段

用户将要输入的信息用data保存并交给fwrite(),fwrite()将内容拷贝到用户缓冲区中,此时并未真正输入,当用户调用fflush()或者缓冲区满了,才会将数据刷入到系统当中。

当内容刷入到内核时,此时会进行系统调用write()。

先通过fd找到struct file对象,通过结构体找到对应的文件inode。此时需要检查inode的数据块指针,若未分配数据块,此时先在数据位图上找到一个空闲的位置进行分配,然后更新指针信息到inode结构体中。

将内容拷贝到缓冲区中,更新inode。

三. fclose阶段

先调用fflush()将用户缓冲区刷新,随后调用系统close()关闭文件描述符,内核将减少file对象的引用计数,如果是最后一个关闭的将释放file对象。inode和dentry会保存在缓存中一段时间方便下次寻找。

三. 软硬链接 

我们通过 In  【选项】  源文件   目标文件    来创建软硬链接

若表示创建软链接选项为   -s

1. 软链接

当我们给一个文件添加软链接时,会产生一个新的独立文件,当然这个独立文件也有自己的inode。

软链接是对文件的一个拷贝,相当于我们计算机桌面的快捷方式一样。

2. 硬链接 

硬链接是对文件inode进行拷贝,它没有独立的inode,它与源文件同用一个inode。当我们对一个文件进行硬链接时,会给该文件进行引用计数加一,当我们删除了其中一个文件名时,引用计数相应的减一。由此我们可以知,硬链接是对文件进行备份处理

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

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

相关文章

小程序开发平台,自主开发小程序源码系统,多端适配,带完整的部署教程

温馨提示&#xff1a;文末有资源获取方式全开源与自主开发源码完全开放&#xff1a;开发者可自由修改前端界面、后端逻辑及数据库结构&#xff0c;支持深度定制&#xff08;如调整用户端交互流程、商家端管理功能等&#xff09;。技术栈透明&#xff1a;基于主流技术&#xff0…

stp拓扑变化分类

Max Age 20sHellotime 2sForward delay 153、拓扑改变需要多长时间1&#xff09;根桥故障&#xff1a;需要50秒&#xff08;Max age2个forwarding delay&#xff09;2&#xff09;非直连链路&#xff1a;非直连故障在稳定的STP网络&#xff0c;非根桥会定期收到来自根桥的BPDU报…

一、深度学习——神经网络

一、神经网络 1.神经网络定义&#xff1a;人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;也简称为神经网络&#xff08;NN&#xff09;&#xff0c;是一种模仿生物神经网络结构和功能的计算模型。人脑可以看作是一个生物神经网络&#xff0c;由…

【牛客算法】 小红的奇偶抽取

文章目录 一、题目介绍1.1 题目描述1.2 输入描述1.3 输出描述1.4 示例二、解题思路2.1 核心算法设计2.2 性能优化关键2.3 算法流程图三、解法实现3.1 解法一:字符串分离法3.1.1 初级版本分析3.2 解法二:数学逐位构建法(推荐)3.2.1 优化版本分析四、总结与拓展4.1 关键优化技…

Maven 继承:构建高效项目结构的利器

一、引言 Maven 是一个强大的项目管理工具&#xff0c;它通过标准化的项目结构和依赖管理极大地简化了 Java 项目的开发流程。在 Maven 中&#xff0c;继承是一种非常有用的功能&#xff0c;它允许我们创建一个父项目&#xff0c;其他子项目可以继承这个父项目的配置信息&#…

Mysql组合索引的update在多种情况下的间隙锁的范围(简单来说)

简单来说&#xff0c;当 UPDATE 语句的 WHERE 条件使用了组合索引&#xff0c;并且需要锁定不存在的“间隙”来防止幻读时&#xff0c;就会产生间隙锁。间隙锁的范围取决于 WHERE 条件如何利用组合索引&#xff0c;以及数据库的隔离级别。 我们用图书馆的例子。比如&#xff1a…

什么是Apache Ignite的affinity(亲和性)

在 Apache Ignite 中&#xff0c; affinity&#xff08;亲和性&#xff09; 是一种用于控制数据分布和查询性能的重要机制。它允许开发者指定数据如何在集群中的节点之间分布&#xff0c;从而优化数据访问和查询效率。以下是关于 affinity 的详细解释&#xff1a;数据亲和性&a…

youtube图论

dfs排序lifo & fifo存储方式邻接矩阵dijstra处理过的保存/更新&#xff0c;意味着一个节点避免了重复访问bfs dfs

借助ssh实现web服务的安全验证

背景 公有云服务器 http 服务 80端口&#xff0c;想做到安全访问无须HTTPS 客户端证书方便、快捷、安全 SSH 隧道 本地代理 使用 SSH 隧道将 HTTP 服务“隐藏”在 SSH 之后&#xff1a; # 客户端建立隧道&#xff08;将本地 8080 转发到服务器的 80 端口&#xff09; ssh…

状态机在前端开发中的艺术:从理论到框架级实践

文章目录一 状态机&#xff1a;复杂逻辑的终结者1.1 什么是状态机&#xff1f;1.2 为何前端需要状态机&#xff1f;二 状态机核心概念深度解析2.1 有限状态机&#xff08;FSM&#xff09;与分层状态机&#xff08;HSM&#xff09;2.2 状态机的数学表示三 前端开发中的状态机实战…

把word中表格转成excle文件

把word中表格转成excle文件 from docx import Document from openpyxl import Workbook from pathlib import Path# 打开 Word 文档 document Document(./weather_report.docx) tables document.tables# 输出文件路径 output_file Path(./weather_report.xlsx)# 如果文件已存…

运维打铁: 阿里云 ECS 实例的高效运维与管理

文章目录思维导图正文内容一、实例基础管理1. 实例创建2. 实例配置调整3. 实例停止与启动二、性能监控与优化1. 系统性能指标监控2. 磁盘 I/O 优化3. 网络优化三、安全防护1. 防火墙设置2. 账号安全管理3. 数据备份与恢复四、自动化运维1. 脚本自动化2. 使用云助手五、成本优化…

RV1126平台(Buildroot Linux)+ SunplusIT SPCA2688 USB摄像头 RTSP推流全流程复盘与问题解决记录

# RK RV1126平台&#xff08;Buildroot Linux&#xff09; SunplusIT SPCA2688 USB摄像头 RTSP推流全流程复盘与问题解决记录一、平台与需求- **硬件平台**&#xff1a;Rockchip RV1126 - **操作系统**&#xff1a;基于Buildroot定制的Linux系统 - **USB摄像头**&#xff1a;Su…

深入理解Java虚拟机:Java内存区域与内存溢出异常

前言Java虚拟机&#xff08;JVM&#xff09;的自动内存管理是其核心特性之一&#xff0c;它极大地简化了开发者的工作&#xff0c;减少了内存泄漏和内存溢出的问题。本文将详细介绍JVM的自动内存管理机制的内存区域与内存溢出异常问题&#xff0c;包括运行时数据区域、对象的创…

位图入门算法191. 位1的个数

题目链接&#xff1a; 191. 位1的个数 - 力扣&#xff08;LeetCode&#xff09; 这道题让我们找出一个数字中二进制中1的个数&#xff0c;这个题目我们就用1的&来解决&#xff0c;最后一位有0为0&#xff0c;都是1才是1&#xff0c;我们只需要判断32次即可。 代码如下&am…

[架构之美]虚拟机Ubuntu密码重置

[架构之美]虚拟机Ubuntu密码重置 当您在虚拟机中运行Ubuntu系统时&#xff0c;忘记密码不再意味着数据丢失&#xff01;本文将详细介绍可靠的密码重置方法&#xff0c;帮助您快速恢复系统访问权限。 一、虚拟机密码重置原理与准备 1.1 为什么虚拟机重置密码更容易 在虚拟机环…

kotlin中withContext,async,launch几种异步的区别

在 Kotlin 协程中&#xff0c;withContext、async 和 launch 是常用的异步/并发操作函数&#xff0c;它们的主要区别在于用途和返回值&#xff1a;1. launch 作用&#xff1a;启动一个新的协程&#xff0c;用于执行不返回结果的并发任务。使用场景&#xff1a;适合执行没有返回…

git 报错fatal: refusing to merge unrelated histories

解决方案在你操作命令后面加--allow-unrelated-histories 例如&#xff1a; git merge master --allow-unrelated-historiesgit pull或者git push报fatal: refusing to merge unrelated histories 同理&#xff1a; git pull origin master --allow-unrelated-histories

Android 13----在framworks层映射一个物理按键

基于Android 13.一、映射步骤确定要映射的物理按键值在kl文件中增加键值对在InputEventLabels.cpp增加AKEYCODE在keycodes.h中定义AKEYCODE值attrs.xml中增加KEYCODEKeyEvent.java中增加KEYCODE在PhoneManagerWindow等相关类中进行拦截处理相关KEYCODE&#xff0c;属于具体的业…

【Java EE】Mybatis-Plus

1. 开始先进行和以前一样的项目配置、数据库连接配置&#xff0c;在这些基础上&#xff0c;额外引入 Mybatis-Plus 依赖即可。<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><vers…