Ext系列文件系统(上)
我们之前说过,文件=内容+属性,这是我们从一个文件得视角去谈的,而系统当中存在成百上千的文件,在系统层面上,文件也就被打开了一小部分,操作是将那么多的文件是进行怎么样的管理呢?更重要的是,我们之前fopen()打开文件的时候,为什么我们的系统就能够准确的把我们想要打开的文件打开,并且可以在系统层面上将文件信息加载到内存?怎么可以做到如此精准?
我们在理解文件是时,单个文件是为内容+属性,而在宏观角度上,文件是被分成:
- 被打开的文件:该话题在基础IO当中已经说明清楚了。
- 未被打开的文件
被打开的文件是在内存当中的,因为操作系统要管理他,如果文件没有被打开呢?他在哪里呢?
答案是在磁盘上!那么没有被打开的文件多多了,我们又是如何将这么大量的文件归类保存到磁盘当中,使我们能快速找到?如果找不到,那我们之前所说的打开一个文件就不能谈起,因为我们默认打开这个文件就相当于该文件能够被找到。
答案是:文件是通过树状的目录结构组织在磁盘上的,这种结构以根目录为起点,逐层展开,形成一个层次化的存储体系。我们可以通过路径来定位文件,路径分为绝对路径和相对路径:绝对路径是从根目录开始的完整路径,而相对路径是从当前目录开始的路径。操作系统(OS)负责管理和组织文件,它通过文件系统来记录文件的位置、属性以及目录结构,确保文件能够被快速、准确地找到和访问。这种机制是操作系统的基本功能之一,也是文件存储和管理的核心。
所以找文件的工作是通过文件系统完成的!!!
我们先从硬件角度开始谈文件系统(磁盘都不了解,谈什么文件系统!?)
本章重点:一个文件没有被打开,在操作系统当中,是如何组织的?
一.理解硬件
1.1磁盘·服务器·机柜·机房
磁盘:传统的机械存储设备,通过磁性介质存储数据。优点是容量大、成本低;缺点是读写速度较慢。
固态硬盘(SSD):基于闪存技术的存储设备,读写速度快,无机械部件,抗震性强。适用于对性能要求较高的场景。
现状:现代笔记本电脑大多使用固态硬盘,而磁盘在服务器领域仍占主流地位。
-
企业级磁盘:容量大、效率高、品质高,用于服务器和数据中心。
-
桌面级磁盘:适用于个人电脑,容量和性能适中。
磁盘用途
-
存储海量数据:磁盘因其性价比高而被广泛用于存储海量数据。
-
冷热数据分离:企业级磁盘分为存储盘和效率盘,根据数据的冷热程度选择不同的存储方案。
机械结构
-
盘片:数据存储在盘片的磁性表面。
-
马达:驱动盘片高速旋转,转速从7200转/分钟到20000转/分钟不等。
-
磁头与摆臂:磁头通过摆臂在盘片半径路径上移动,进行数据的读写操作。
类比说明:磁盘的工作原理类似于VCD/DVD光盘,但磁盘可以进行读写操作,而光盘只能读取。
服务器
-
服务器通常没有鼠标、键盘和显示器,主要通过网络进行交互。
-
服务器内部可以插入多块磁盘,用于存储数据。
-
例如,一台服务器可以插入24块4TB的磁盘,总存储容量为96TB。
机柜
-
机柜用于存放多台服务器,便于管理和散热。
-
一个机柜可以容纳多台服务器,通过网络连接实现数据交互。
机房
-
机房由多个机柜组成,是数据中心的核心部分。
-
机房内的服务器用于部署各种服务,通过网络对外提供服务。
存储方式:冷热数据分离
-
热数据:频繁访问的数据,存储在高性能的存储设备上,如SSD。
-
冷数据:不常访问的数据,存储在大容量、低成本的磁盘上。
-
示例:微信朋友圈的早期记录可能会被备份到低成本的存储设备上。
存储设备(磁盘和固态硬盘)是计算机系统的重要组成部分,尤其在服务器和数据中心中扮演关键角色。磁盘因其大容量和低成本被广泛用于存储海量数据,而固态硬盘则在性能上有优势。服务器通过机柜和机房组成数据中心,用于部署各种服务。企业级磁盘和存储方案的选择需要根据数据的冷热程度和性能需求进行优化。国内在存储技术上逐步推进国产化,以满足信息安全需求。
1.2磁盘物理结构
磁盘表面与存储单元:
-
磁盘表面看起来光滑,但由许多微小的存储单元(类似小磁铁)构成。
-
每个存储单元通过磁极(南极和北极)来表示二进制数据(0和1)。
磁盘写入原理:
-
写入数据时,磁头通过改变存储单元的磁极方向来存储二进制信息。
-
磁盘的存储是永久性的,除非主动删除或破坏磁盘。
-
主轴(马达):主轴是硬盘内部的一个关键部件,它通过马达驱动磁片旋转。主轴的转速通常以每分钟转数(RPM)来衡量,如7200 RPM,转速越高,硬盘的读写速度通常越快。
-
磁盘(相当于纸):磁盘是硬盘中用于存储数据的磁性材料层。数据通过改变磁盘上特定区域的磁性来记录,类似于在纸上写字。
-
磁头(相当于笔):磁头是负责读写磁盘上数据的部件。它通过改变磁盘上磁性区域的极性来写入数据,或者通过检测磁性区域的极性来读取数据。
-
磁头臂:磁头臂是连接磁头和主轴的机械臂,它负责将磁头移动到磁盘的正确位置,以便进行数据的读写操作。
-
永磁铁:永磁铁用于辅助磁头臂的移动,确保磁头能够准确地定位到磁盘的特定位置。
这些部件共同工作,使得硬盘能够存储和检索大量数据。硬盘的工作原理可以类比为一个记录系统,其中磁盘相当于纸,磁头相当于笔,而主轴和磁头臂则确保“笔”能够在“纸”上正确地写入和读取信息。
磁盘的耐用性与破坏方式:
-
磁盘在密封状态下具有很高的耐用性,内部接近真空状态。
-
如果需要彻底销毁磁盘数据,可以通过高温加热使其退磁。
1.3磁盘的存储结构
主轴(Spindle):硬盘中心的旋转轴,盘片固定在主轴上,通过主轴的旋转使盘片高速旋转。
盘片(Platter):硬盘中用于存储数据的磁性材料,数据以磁性形式存储在盘片表面。
磁道(Track):盘片表面的同心圆,每个同心圆是一个磁道,用于存储数据。
面(Surface):每个盘片有两个面,即上表面和下表面,都可以存储数据。
扇区(Sector):磁道被分割成的扇形区域,是硬盘存储数据的基本单位,通常为512字节。
其实硬盘通常包含多个盘片,每个盘片都有两个存储面的,即上表面和下表面,都可以用于存储数据。在多盘片的硬盘中,所有的盘片都堆叠在一起,并且它们共享同一个主轴,因此会同步旋转。
-
柱面(Cylinder):所有盘片中位于同一半径的磁道组成的集合称为柱面。当磁头移动到某个柱面时,所有盘片上对应的磁道都会被定位。
-
磁头(Head):每个盘面对应一个磁头,用于读取和写入数据。所有磁头都连在同一个磁臂上,因此所有磁头只能“共进退”。
-
磁头臂(Actuator Arm):连接磁头和主轴的机械臂,负责将磁头移动到磁盘的正确位置。
每个盘片的每个存储面都有一个对应的磁头,用于读写该面上的数据。磁头安装在磁头臂上,磁头臂可以沿半径方向移动,以便磁头可以访问不同半径上的磁道。当磁头臂移动时,所有磁头会同步移动到对应的磁道上,因为它们都是连接在同一磁头臂上的。
因此,在多盘片硬盘中,可以同时从多个盘片的不同磁道上读写数据,这增加了数据访问的效率。每个盘片的所有磁道组成一个柱面,当磁头移动到某个柱面时,所有盘片上相同半径位置的磁道都会被定位,允许并行读写多个盘片上的数据。
数据存储单位:扇区(Sector):硬盘存储数据的基本单位,通常为512字节。操作系统在读写硬盘时,必须以扇区为单位进行操作。(并不是1比特,不然IO效率得多慢呀!)
数据定位方式:
-
柱面(Cylinder):所有盘片中位于同一半径的磁道组成的集合称为柱面。当磁头移动到某个柱面时,所有盘片上对应的磁道都会被定位。
-
磁头(Head):每个盘面对应一个磁头,用于读取和写入数据。所有磁头都连在同一个磁臂上,因此所有磁头只能“共进退”。
-
扇区(Sector):磁道被分割成的扇形区域,是硬盘存储数据的基本单位。
数据读写操作:
-
磁盘定位:硬盘通过磁头臂的移动来定位到特定的柱面,然后通过磁盘的旋转来定位到特定的扇区,从而实现数据的读写。
-
读写操作:在写入数据时,数据会被写入到一个或多个扇区中。硬盘通过记录每个扇区的柱面、磁头和扇区号(CHS地址)来定位文件。
企业级硬盘与桌面级硬盘:主要区别在于寻址速度和精度。企业级硬盘通常具有更快的转速和更精确的磁头定位能力,因此性能更高。
硬盘的物理结构和数据存储方式是通过一系列的机械部件(如磁盘、磁头、磁头臂等)和逻辑结构(如磁道、扇区、柱面等)共同实现的。硬盘的读写操作基于扇区进行,而文件的存储则是通过记录文件所占用的扇区的CHS地址来实现的。硬盘的性能受到其机械结构和电子控制的影响,企业级硬盘通常具有更高的性能和可靠性。
可以使用fdisk
命令查看硬盘的分区信息,包括扇区大小、磁盘类型等:
磁盘容量计算:磁盘容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数。
文件系统通过记录文件所占用的扇区的CHS地址来实现文件的存储和管理。但是CHS寻址还是有不足:
寻址限制:CHS寻址方式支持的硬盘容量有限。由于系统用8位存储磁头地址,用10位存储柱面地址,用6位存储扇区地址,一个扇区为512字节,因此使用CHS寻址一块硬盘最大容量为256 × 1024 × 63 × 512字节 = 8064 MB(约8.4 GB)。
CHS寻址方式是硬盘数据定位的一种传统方法,通过柱面、磁头和扇区的组合来定位数据。然而,由于寻址位数的限制,CHS寻址方式支持的硬盘容量有限,最大约为8.4 GB。现代硬盘通常采用更先进的寻址方式,如LBA(逻辑块寻址),以支持更大的存储容量。
但是计算机不是我们想的直接用磁盘,直接拿着CHS这三个参数直接进行地址的访问,所以我们接下来对磁盘做逻辑抽象。(硬件->系统的过度)
1.4磁盘的逻辑结构
1.4.1理解过程
磁带上⾯可以存储数据,我们可以把磁带“拉直”,形成线性结构。
说明了磁盘的逻辑存储结构可以类比为卷在一起的磁带,其中每个存储单元(扇区)可以看作是磁带上的一个线性块:
![](https://i-blog.csdnimg.cn/direct/40ed7d2afbd540cb982550ab4bda6f2d.png)
这就是先描述再组织,对磁盘的管理,转化成了对数组的增删查改!!!
但是因为由于磁盘的物理结构,磁盘在本质上进行定位的时候,依旧是在自己内部使用CHS,但是我们可以抽象成线性的!
这样每⼀个扇区(基本单元),就有了⼀个线性地址(其实就是数组下标:定位一个扇区,只是数组下标),这种地址叫做LBA:
当代我们的磁盘不再直面上让操作系统直接使用CHS寻址了,清一色全是LBA,磁盘认识LBA,那么磁盘会拿着LBA地址在内部转化成CHS,然后帮我们自动寻址,我们现在虽然还不知道LBA和CHS是怎么互相转化的,我们后面会讲,但是我们心里要知道,从今天开始,磁盘在我看来就是一个黑盒,我给他喂了一个LBA地址,他就要给我在磁盘特定的扇区里读写数据了。
那有那么多扇区,操作系统是不是要将所有的扇区对应的LBA地址记录下来呢?
其实是没有必要的,但是操作系统依旧可以使用任意一个LBA,因为扇区大小是固定的,换句话说,其实我作为操作系统,我在开机的时候,只要能够识别出来这块磁盘的总容量,每一个扇区的地址我便都有了。(得到了地址范围)
我们下面来谈谈真实的过程。
1.4.2真实过程
![](https://i-blog.csdnimg.cn/direct/7803fa8623254ce7a2c8a42451457894.png)
![](https://i-blog.csdnimg.cn/direct/b1cc6802b41e48908730ee35cfadeb42.png)
![](https://i-blog.csdnimg.cn/direct/9178ee907a94413292d7aa6c95231859.png)
也就是一维数组!!!
柱面!整个磁盘所有盘面的同一磁道,即柱面展开:
- 柱⾯上的每个磁道,扇区个数是⼀样的!!!
- 这不就是⼆维数组吗!!!
整盘:
整个磁盘不就是多张⼆维的扇区数组表(三维数组?)
所有,寻址⼀个扇区:先找到哪⼀个柱⾯(Cylinder) ,在确定柱⾯内哪⼀个磁道(其实就是磁头位置,Head),在确定扇区(Sector),所以就有了CHS。
我们之前学过C/C++的数组,在我们看来,其实全部都是⼀维数组:
1.5CHS && LBA地址
CHS转成LBA:
-
磁头数*每磁道扇区数 = 单个柱面的扇区总数
-
LBA = 柱面号C单个柱面的扇区总数 + 磁头号H每磁道扇区数 + 扇区号S - 1
-
即:LBA = 柱面号C*(磁头数每磁道扇区数) + 磁头号H每磁道扇区数 + 扇区号S - 1
-
扇区号通常是从1开始的,而在LBA中,地址是从0开始的
-
柱面和磁道都是从0开始编号的
-
总柱面,磁道个数,扇区总数等信息,在磁盘内部会自动维护,上层开机的时候,会获取到这些参数。
LBA转成CHS:
-
柱面号C = LBA // (磁头数*每磁道扇区数)【就是单个柱面的扇区总数】
-
磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数
-
扇区号S = (LBA % 每磁道扇区数) + 1
-
"//": 表示除取整
所以:从此往后,在磁盘使用者看来,根本就不关心CHS地址,而是直接使用LBA地址,磁盘内部自己转换。所以:
从现在开始,磁盘就是一个元素为扇区的一维数组,数组的下标就是每一个扇区的LBA地址。OS使用磁盘,就可以用一个数字访问磁盘扇区了。
硬件解释清楚了,接下来就跟硬件没有关系了,我们该慢慢进入软件,也就是操作系统该如何管理磁盘了。
二.引入文件系统
2.1引入“块”概念
其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
与块设备相对应的是字符设备,比如说键盘,显示器就是典型的字符设备,字符设备和块设备最重要的区别是字符设备不支持随机读取,就像我们在键盘输入"abcd",怎么输入的,顺序一样,在程序当中就是怎么读的,而且读取时,是以字符为单位进行一一读取的。
所以文件系统访问磁盘不以扇区为单位,而是以“块”为单位。
硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的⼤⼩是由格式化的时候确定的,并且不可以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个 ”块”。”块”是⽂件存取的最⼩单位。
-
物理层面:磁盘的物理结构决定了数据的存储是以扇区为单位的,磁盘驱动器在硬件层面只能以扇区为单位进行读写。
-
逻辑层面:操作系统为了提高数据管理效率和读写性能,引入了块的概念。块是操作系统与磁盘交互的最小单位。(也是处于软件和硬件解耦的考量,如果你让OS直接用磁盘的扇区的话,万一磁盘技术发生更新的话,那么OS你也会更改)
- 磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区
- 每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。
- 知道LBA:块号 = LBA/8
- 知道块号:LAB=块号*8 + n. (n是块内第⼏个扇区)
2.2引入“分区”概念
想象一下,如果让你管理一片800GB的“数据国土”,你会怎么做?直接管理每一个小小的存储单元,就像试图用中央政府去管理每一寸土地一样,成本和难度都高得吓人!但聪明的人类早就找到了解决办法——分区。
就像把广袤的国土划分为不同的省份,我们把800GB的磁盘划分为几个区域,比如300GB、300GB和200GB:
这样,管理一个分区的方法就可以复制到其他分区,大大降低了工作量。但这还不够,我们还可以进一步把每个分区划分为更小的“城市”——也就是分组。比如把300GB的分区再分成10个30GB的小块,只要管理好一个30GB的小块,其他的小块也可以用同样的方法搞定:
我们接下来就是重点在于分区分组后的其中一组的内容!!!我们还要注意,因为文件=内容+属性,在Linux下,内容和属性是分开存储的!!!在我们所有的分组当中,基本单位是块哦,是4KB,我们文件的内容是在分组后的成员Data Blocks当中。假设文件内容大小为17KB,那么就占其5个块。(这个是很重要的,说一个题外话,现在我们还在谈文件系统,后面将文件系统,通信这样的话题谈完,我们到后面多线程的时候,我们到时候会谈磁盘和内存之间是如何进行交互的,也就是内存管理的基本模式是什么,到那时候,我们会知道其实内存管理也是以4KB为单位的,也就是说我们磁盘上面的数据块,天然的就是为了随时载入内存而做准备的!)属性就是在inode Table当中了,我们接下来就要引入其相关概念了。
这种“分而治之”的思想,不仅让管理变得简单,还让整个磁盘的管理变得高效。在Windows系统里,正常来说只有一块磁盘,磁盘进行分区,这些分区被显示为C盘、D盘、E盘等;分区从实质上说就是对硬盘的⼀种格式化。但是Linux的设备都是以⽂件形式存在,那是怎么分区的呢?
柱⾯是分区的最⼩单位,我们可以利⽤参考柱⾯号码的⽅式来进⾏分区,其本质就是设置每个区的起始柱⾯和结束柱⾯号码。 此时我们可以将硬盘上的柱⾯(分区)进⾏平铺,将其想象成⼀个⼤的平⾯,如下图所⽰:
2.3引入“inode”概念
在深入了解Linux文件系统之前,我们先来探讨一个核心概念——inode。inode(索引节点)是理解Unix/Linux文件系统和硬盘存储的基础。
inode是文件系统中用于存储文件元数据的数据结构,它并不存储文件的实际数据内容,而是记录与文件相关的各种信息。每个文件和目录在文件系统中都有一个唯一的inode与之关联。
[root@localhost linux]# ls -l
总⽤量 12
-rwxr-xr-x. 1 root root 7438 "9⽉ 13 14:56" a.out
-rw-r--r--. 1 root root 654 "9⽉ 13 14:56" test.c
ls -l读取存储在磁盘上的⽂件信息,然后显⽰出来。
其实这个信息除了通过这种⽅式来读取,还有⼀个stat命令能够看到更多信息:
[root@localhost linux]# stat test.c
File: "test.c"
Size: 654 Blocks: 8 IO Block: 4096 普通⽂件
Device: 802h/2050d Inode: 263715 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-09-13 14:56:57.059012947 +0800
Modify: 2017-09-13 14:56:40.067012944 +0800
Change: 2017-09-13 14:56:40.069012948 +0800
到这我们要思考⼀个问题,⽂件数据都储存在”块”中,那么很显然,我们还必须找到⼀个地⽅储存⽂件的元信息(属性信息),⽐如⽂件的创建者、⽂件的创建⽇期、⽂件的⼤⼩等等。这种储存⽂件元信息的区域就叫做inode,中⽂译名为”索引节点”。
inode
结构体定义在Linux内核源码的 include/linux/fs.h
文件中:
以下是Linux中inode
结构体的主要内容,以简洁的代码形式展示,并附上简要注释:
struct inode {
umode_t i_mode; // 文件类型和权限
kuid_t i_uid; // 文件所有者的用户ID
kgid_t i_gid; // 文件所有者的组ID
loff_t i_size; // 文件大小,以字节为单位
struct timespec64 i_atime; // 文件最后访问时间
struct timespec64 i_mtime; // 文件最后修改时间
struct timespec64 i_ctime; // inode最后更改时间
unsigned int i_nlink; // 硬链接计数
dev_t i_rdev; // 设备文件的主次设备号
unsigned long i_ino; // inode编号,文件系统中唯一标识
const struct inode_operations *i_op; // inode操作函数表
struct super_block *i_sb; // 所属超级块
struct address_space *i_mapping; // 文件数据的地址空间映射
};
我们不同文件都要有属性的,只是属性的内容不同,由于每一个struct inode的大小固定为128字节,所以文件的属性大小是一样的。
inode中存储的信息包括:
-
文件类型(普通文件、目录、链接等)
-
文件大小(以字节为单位)
-
文件所有者的用户ID(UID)和组ID(GID)
-
文件的读、写、执行权限
-
时间戳:包括文件的创建时间(ctime)、最后修改时间(mtime)和最后访问时间(atime)
-
链接数:记录有多少个文件名指向这个inode
-
数据块指针:指向存储文件实际内容的磁盘数据块
当中一些链接,我们会在软硬链接讲到。
在Linux文件系统中,文件名和inode是分离的,也就是说文件名不会作为属性,保存在文件的inode中。(也是保证了文件的属性大小是一样的,因为文件名字符数组大小不定;当然还有其他考量,我们接下来会说。)