🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。
🎯每天努力一点点,技术变化看得见
理解文件系统
在计算机中存在着已经被打开的文件,也存在着没有打开的文件。由于内存空间较小,资源较为紧张,故没有打开的文件都会存储在磁盘上。此时,我们会关注哪些问题呢?无非是文件如何存储在磁盘上的,如何获取该文件的属性和文件内容,磁盘上读取文件效率,不同路径上的文件如何被分门别类地存储在磁盘上的。下面,我们就一起聊聊文件系统中如何管理各个文件的。
物理角度认识磁盘
磁盘是计算机上唯一的机械设备,但现在计算机多采用固态硬盘(SSD)替代了磁盘,这里仅讨论磁盘。磁盘是外设中的一种,它是通过磁盘的磁性(N/S级)存储0或1,属于永久性存储介质。(ps:非永久性存储设备,如内存,其为掉电易失设备)
每个磁盘由多个盘片组成,每个盘面的上下两面均是光滑的,均可以存储数据。磁盘需要借助磁头来读取数据,故每个面需要一个磁头,即一个盘片上的上下两个盘面均有一个磁头(一个盘面合计2个磁头)。而磁盘读取盘面上某个位置的数据时,通过摆动磁头和转动盘面来实现。
磁盘中的每个盘面,以中心向外,划分了不同的同心圆,这些同心圆称为磁道,如下图左侧所示。两个磁道与两半径构成的扇形区域称之为扇区,如下图右侧所示。磁盘被访问的基本单元就是扇区,扇区的大小一般为512字节或4KB。我们可以把磁盘看作无数个扇区构成的存储介质。
在磁盘中,定位某个扇区的方法是:①磁头左右摆动定磁道(也称为柱面);②确定数据在哪个盘面,确定具体从哪个磁头读取数据;③盘面转动确定扇区。这种方法简称为CHS(Cylinder、Header、Sector)。
【示例】若要读取下图左侧中,橙色方块处的数据(假设已经确定使用哪个磁头读取的情况下)。
①磁头摆动到指定磁道上,即确定读取的磁道。
②一个磁道上,包含许多个扇区,需要通过转动盘面来确定扇区。转动后,如右图所示,此时可以读取数据存储区域的内容了。
由于磁盘属于机械设备,它的整体速度较慢。故在使用磁盘时,要让磁盘运动越少,效率才能越高;若磁盘运动越多,则效率越低。同时,我们也意识到:具有相关性的数据要存储在一起,这样可以减少磁盘运动。
逻辑角度认识磁盘
磁盘寻址
在十年前左右,流行使用磁带听读英语。磁带中的塑料带被卷成圆圈,但实际读取是线性读取的。如下图右侧所示,当我们将磁带中的塑料袋拉出来,可以看出,它存储数据是线性存储的。
我们也可以将磁盘看作线性结构。即将磁道上的扇区按照某种规则,给予线性顺序编号。如下图所示,从左向右依次是盘面1、盘面2、盘面3…;每个盘面内,从左向右分别是该盘面的不同磁道;每个磁道又由多个扇区组成。(这里我们假设每个磁道的扇区个数相同)
【示例】假设有一个磁盘,它的每个盘面有50个磁道,每个磁道有400个扇区(即一个盘面有20000个扇区)。若某个文件存储在第28888号扇区中,则它属于哪个盘面的哪个磁道的哪个扇区呢?
盘面号 = 28888 / 20000 = 1号盘面(即第2个盘面),磁道号 = 8888 / 400 = 22 号(即第23个磁道),扇区号 = 8888 % 400 - 1 = 87号扇区(即第88个扇区)。
像上述示例中寻找扇区的方式,称为LBA寻址方式(又称为逻辑寻址方式)。
磁盘中的寄存器
不仅CPU中包含寄存器,其他外部设备中也存在寄存器。如果某个进程要向磁盘中写入数据,则需要将磁盘中的r/w寄存器标志为写状态,将要写入的数据放入数据寄存器中,将要写入的地址空间首地址写入地址寄存器。当磁盘完成当前读写操作后,磁盘会将状态寄存器改为读写失败或读写成功(在读写时,则标志为正在读写)。
ps:这里仅仅要说明,磁盘中也包含寄存器,并未列出磁盘中的所有寄存器。
磁盘存储管理
由于一个磁盘中包含大量的扇区,为了方便管理,我们可以将一个磁盘划分为多个扇区。但每个分区依然包含许多扇区,我们可以进一步将每个分区划分为多个块组(Block Group),每个块组中包含该块组的数据存储情况及具体的数据。
假设,有一个800GB的磁盘,先将其分为5个区,第1个200GB,第2个100GB,第3个150GB,第4个150GB,第5个200GB。
由于每个分区的存储空间依然很大,每个分区被进一步分为不同的块组(Block Group)。
★ps:在某些分区中会在分区开头存储一个Boot Block(引导块),Boot Block用于存储引导加载程序,这个程序负责在系统引导时加载操作系统,并启动计算机。Boot Block用于启动系统,是关键的数据,它在其他分区中也可能被重复存储,以保证某个分区存储的Boot Block出问题时,能从其他分区读取并启动系统。
而每个块组(Block Group)开始处包含着该块组的描述信息,描述信息后面才是存储的具体数据。每个块组第一个存储的Super Block(超级块),而接下来的Group Descriper Table包含了当前块组的信息,包含剩余的块数、inode数量等。接下来,我们再来聊聊余下的4个区域:Block BitMap、Inode BitMap、InodeTable及Data Block。
★ps:Super Block是文件系统中的一个关键数据结构,用于存储文件系统的整体信息,包括文件系统的大小、单个数据块大小等;Super Block提供了文件系统的基本参数和元数据,使操作系统能够正确地管理文件和目录(为了保证文件系统能安全运行,避免因当前Super Block存储区域错误导致文件系统无法访问,故Super Block也被存储了多份)。
文件的数据包含文件属性及文件内容。Linux 系统在磁盘存储文件时,将文件的属性和数据分开存储。其中,文件的内容存储在数据块中(Data Block,以块的形式程先,常见的块大小为4KB),而文件的属性存储在Inode中(Linux系统中规定,单个文件的所有属性占128字节)。
一个文件有特定的一组属性,而属性存放于Inode中,即一个文件配有一个Inode。为了保证Inode的唯一性,每个Inode的编号是唯一的。我们可以通过ls -li
查看当前目录下所有文件的Inode↓↓↓
也可以通过stat [文件名]
查看某个文件的Inode↓↓↓
Inode中保存着对应文件的文件类型、权限、引用计数、所属组、所有者、ACM时间、文件存储在哪些数据块中等。
★注意:Linux系统中,文件的属性不包含文件名。
★ps:Linux系统中,一个文件一个Inode,每一个Inode都有自己的Inode编号。Inode的设置,是以分区为单位的,不能跨分区。
Inode存储文件的所有属性,文件名不属于Inode内的属性!那我们怎么知道一个文件的Inode的编号?使用者从来没有关心过Inode,用的都是文件名。
关于上面这个问题,我们就需要聊聊Linux中的目录了!那怎么理解目录呢?目录本质也是文件,也有自己的inode,因为目录也有自己的属性↓↓↓
而目录也有自己的存储内容,目录的内容中存储着该目录中包含了哪些文件。既然有数据,故目录也要有自己的数据块。那里面存的具体的内容就是它所包含的各个文件名与其Inode的映射关系。因而,使用者只需要使用文件名就可以访问一个文件了(因为有了文件名,就能在当前目录的数据块中找到该文件名映射的Inode,也就能访问该文件了)。
有了上面的认识,我们可以总结出以下内容:
- ①为什么同一个目录下不能存在同名文件呢?
因为文件名与Inode的映射关系唯一,如果存在同名文件,则会存在多个文件映射到用一个Inode的情况,故一个目录下不能存在同名文件。- ②如果没有一个目录的w权限,则不能在该目录下创建文件,这是为什么?
在文件中创建文件,需要将文件名与Inode的映射关系写入该目录的数据块中,由于没有w权限,故无法写入数据块。- ③如果没有一个目录的r权限,则不能查看目录中的文件存储情况,这是为什么?
要查看目录中存储的文件情况,则需要读取目录的数据块,由于没有r权限,则不能读取。- ④如果没有一个目录的x权限,则不能进入该目录,这是为什么?
可以将目录假想成一个程序,要进入这个目录就要执行这个目录,由于没有x权限,故无法进入。
BlockBitMap:比特位的位置与块映射起来,比特位的内容表示该块是否被使用。
InodeBitMap:比特位的位置和Inode编号映射起来,比特位的内容表示该Inode是否有效(是否被使用)。
- 新建一个文件,系统做了什么?
①在InodeBitMap申请一个Inode;
②在BlockBitMap申请当前文件需要的对应数量的Data Block,并在这些Data Block中存储文件的内容;
③并在InodeTable中找到申请的Inode,存储该文件的信息及申请了哪些Data Block;
④在当前目录的Data Block保存该文件的文件名与Inode的映射关系。
- 删除一个文件,系统做了什么?
①将文件存储内容的块在BlockBitMap中的对应位置设置为无效;
②将文件存储属性的Inode在InodeBitMap中的对应位置设置为无效;
③删除当前目录的数据块中存储的该文件与Inode的映射关系。
ps:不需要情况Inode和Data Block中的数据,等下一个文件直接覆盖该文件的内容即可。
- 修改or查询一个文件,系统做了什么?
①在当前目录的数据块中,通过文件名找到该文件的Inode;
②从Inode中获取文件的属性信息及文件内容存储在哪些Data Block中;
③返回文件属性及文件内容。
★ps:为什么拷贝数据慢,而删除数据快呢?
拷贝数据数据时,需要将数据挨个比特位写入到文件对应的Data Block中;而删除数据的时候,不需要情况Data Block中的数据,只需要将该文件的Data Block对应在BlockBitMap中的位置置为无效即可。
★ps:为什么系统提示还有空间,但却无法创建文件呢?
Inode还有但DataBlock没有了,或Inode没了但DataBlock还有。
软链接与硬链接
软链接
建立软链接的方法
ln -s [file-name] [soft-link-name]
我们可以通过软链接向file1中写入"Jammingpro"↓↓↓
我们再来看一下file1与soft-file1的inode↓↓↓
由上面的结果可知,软链接是一个独立的文件,因为它有独立的inode,故它们的数据块(Data Block也是不同的)。软链接中存储的是file1的路径。
当访问软链接时,它会到对应的路径中找到file1的文件名与inode的映射关系,并访问对应的inode。软链接就类似与Windows中的快捷方式(它在Linux系统中的应用也是用于创建快捷方式)↓↓↓
如果要删除软链接,除了使用rm [软链接名称]
,也可以使用unlink [软链接名称]
↓↓↓
硬链接
创建硬链接的方法
ln [file-name] [hard-link-name]
从上面的建立结果可以看出,file1在创建硬链接前的引用计数为1,当建立硬链接后的引用计数变为了2。我们再来看看者两个文件的信息↓↓↓
两个文件除了文件名不同,其他信息(Inode编号,占用数据块数量、大小等)均相同。故所谓建立硬链接,本质其实就是在特定的目录的数据块中新增文件名和指定文件的Inode编号的映射关系,并将对应文件的引用计数+1。也就是说,硬链接就是给对应的文件取别名。
★ps:任意一个文件(无论是目录还是普通文件)都有Inode,每个Inode内部都有一个叫做引用计数的字段。只有该文件的引用计数变为0时,该文件的Inode及Data Block才会被删除。
硬链接用途有什么呢?关于它的用途,我们需要先来探索关于目录的一些问题↓↓↓
为什么我们创建文件夹的使用,它的引用计数为2呢?
下图查看了dir的Inode,并进入dir中,查看了.的Inode。可以看出,dir的Inode与.的Inode是相同。也就是说,当我们创建目录时,会在该目录中自动创建一个该目录的硬链接,其名称为.。
如果在dir目录中创建一个名为start的目录,可以发现,.的引用计数变为3。这是为什么呢?
进入start目录,可以发现该文件夹中的…的Inode与上一级目录中的.的Inode编号相同,也就说:每当在当前目录下创建一个目录,该目录中会自动创建一个名为…的上一级目录的硬链接。
通过上面我们可以知道,硬链接可以用于目录的跳转。因为我们使用.、…来跳转目录。但Linux并不允许用户给目录创建硬链接,这是为什么呢?
假如我们将某个目录中的.和…均改为根目录,则此时会出现环结构。当我们使用搜索指定时,就会出现无穷循环的现象。
🎈欢迎进入从浅学到熟知Linux专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d