Linux学习:文件系统
目录
- 1. 前言
- 2. 磁盘的相关背景
- 3. 磁盘的物理存储
- 3.1 磁盘的物理结构
- 3.2 磁盘的硬件寻址方案(CHS定位法)
- 4. 磁盘的逻辑存储(文件系统)
- 4.1 磁盘的LBA地址
- 4.2 磁盘空间管理的"分治"思想
- 4.3 块组结构与磁盘文件的存储方式
- 4.4 再次理解文件操作
- 5. 软硬链接
- 5.1 软硬链接的指令
- 5.2 软硬连接的原理
- 5.3 软硬连接的应用场景
1. 前言
Linux操作系统下一切皆文件,因此,程序我们也可以将其理解为一种文件,进程是被加载进入内存调度运行的程序,所以之前对于进程的学习我们也可以理解为是针对被打开文件的学习。可是,操作系统中并非所有的文件都是被打开的,反而,大部分的文件都是没有被打开的状态,这些大量的未打开的文件都存储在磁盘上,因此,未被打开的文件我们也可以称之为磁盘文件。
操作系统是系统资源的管理者,它的职能就是管理统筹好各方面的计算机资源为上层,为用户提供稳定可靠的服务和环境,这些大量的未打开文件也隶属于系统资源中重要的一部分,所以操作系统也要对其进行相应的管理,通过管理使得操作系统可以快速的定位获取磁盘上的文件资源。在此前,对于操作系统目录结构的简单学习我们获知,对于计算机中所有的文件资源都可以通过路径的方式去定位与访问,可这只是操作系统提供给我们用户应用上的使用形式,而操作系统实际上是如何进行实现磁盘文件的管理呢,接下来,我们就来针对于这里的相关知识进行学习,文件系统。
2. 磁盘的相关背景
在正式开始对于文件系统的学习之前,为了更好更深入地学习文件系统,我们先来简单了解一下存储未打开文件的硬件设备,磁盘,这一设备的物理结构。
对于未打开文件的存储当前有两种主流的存储设备,为磁盘与SSD,SSD作为新兴的存储弥补了磁盘怕抖动怕刮花等缺陷。磁盘可以大体分为两类,一是桌面级磁盘,此类磁盘大多只有一个盘面,已经被大批淘汰,只有少数台式机还在使用,二是企业级磁盘,一般存在多个柱头和盘面,能够存储大量的数据,在企业中还在大范围应用。
为何SSD各方面的性能都要优于磁盘,可是在企业中,磁盘还是没有被淘汰掉呢,这是因为SSD虽然性能优异可以相对的造价也高,而企业级磁盘呢不仅存储量大,而且造价便宜性价比高。
磁盘的背面会有一个集成电路板,我们称之为磁盘的伺服系统,其可以识别一些简单的指令,诸如控制磁头与盘片,开关机等。此外,背面还存在着一个铜片插口,接入内存,通过这一插口来进行数据的交互,读取写入。
在之前的学习中,我们得知数据在计算机中是以二进制的形式去存储的,但二进制的概念只是人为规定出来的,实际在物理实现上,不同的设备达成这一标准的方式各有不同。内存则是采用触发器的方式来表示储存数据,需要不断向触发器中充电,虽然数据的读取速度远超磁盘,可同时也意味着断电之后,数据会全部丢失。磁盘则是采用有磁性的材料制造,通过磁性南北极的不同来0和1,磁盘的磁性往往能保存很久,短则几年,长则几十年,所以磁盘也常常被称为永久性存储介质。,磁盘在存储数据的时效性上具有优势,可因为其是通过机械运动来读取数据所以也就导致,其读取数据的速度很慢。
大型互联网公司中,往往存储着很多用户数据,在企业磁盘的定期淘汰时,需要进行集中报废处理,防止数据泄露。
3. 磁盘的物理存储
3.1 磁盘的物理结构
磁盘结构中,用于数据读写的主要部件为盘片与磁头,盘片负责存储数据,磁头负责读写数据,在正式来学习磁盘的物理存储方式之前,我们先来了解一下这两个重要的磁盘部件。
盘面和磁头是一一对应的,一个盘片会配备一个相对应的磁头负责数据读写,磁盘的盘片两面都可进行读写。盘片的具体结构,我们可以理解为是一个个同心圆环嵌套而成,最中心的镂空部分用于嵌套音圈马达,通过马达带动盘片高速旋转。接下来,我们来看三个关于磁盘读写的重要概念:
- 磁道/柱面(cylinder):构成空心圆盘的一个个嵌套起来同心圆环。
- 磁头/盘面(head):磁头与其进行读写的盘面是成对对应的,其结构就是上面图片中的整个空心圆盘。
- 扇区(sector):磁盘中的IO基本单位,但不一定是操作系统中的IO基本单位,扇区大小一般为513Byte,4KB。
我们可以看到上面关于磁盘的结构图中,扇区的面积越靠外侧越大,但大部分情况下,磁盘的每个扇区存储的数据都是相同,这一点是通过调整扇区内微型磁铁密度达成的。当下也出现了可以使得扇区存储大小不同的磁盘。
3.2 磁盘的硬件寻址方案(CHS定位法)
企业级的磁盘一般都拥有多个盘面与磁头,这样就可以存储更多的数据。磁盘的基本IO单位是扇区,磁盘的读写方式是通过磁头的左右摆动配合盘片的高速旋转,磁头的左右摆动是为切换到不同的磁道上,而盘片的高速旋转则是为了让磁头读取到磁道上的所有扇区。
在读写数据时,磁头与盘面不是也不能直接接触的,否则会使得盘面刮花使得数据丢失,两者的相对间隔,大致就比如一架波音747在离地一米的高度进行飞行。因此,在磁盘的制造过程中一定要做到无尘,极小的一粒灰尘在磁盘上其的直径就已经超过"1米"了,同时磁盘也要具有一定的抗抖性,否则在运行中轻微的抖动也就会导致磁盘的刮花。
磁道,磁头,扇区都拥有相对应唯一编号,在磁盘中我们确定扇区位置时,就是通过这三个编号(哪个盘面,哪个磁道,哪个扇区)来标识定位每一个扇区的,这种定位方法我们称之为CHS定位法。
4. 磁盘的逻辑存储(文件系统)
4.1 磁盘的LBA地址
操作系统对于系统资源的管理都依照,先描述,再组织的准则,对于磁盘空间的管理也不例外,逻辑上,我们可以将磁盘的一圈圈圆环磁道拉直然后首尾相接,这样我们就完成了对磁盘的逻辑描述,将其转化为了一个线性数组,这个数组的元素为扇区,此时,操作系统对于磁盘的管理就转换为了对于顺序表的管理。
下面,我们来简单了解一下关于sector数组元素下标转换为CHS地址的方法:
- sector / 单盘扇区个数 = H(所在哪个扇区)
- sector % 单盘扇区个数 = temp
- temp / 一个磁道上的扇区个数 = C(哪个磁道)
- temp % 一个磁道上的扇区个数 = S(哪个扇区)
操作系统(OS)认为,一次与磁盘IO一个扇区(512Byte)大小的数据太小了,而且因为磁盘的读写速度很慢,所以OS在与磁盘的IO过程中会进行预加载,一次以4KB为基本单位来进行IO操作。因此,我们可以将刚刚上面抽象出的sector数组进一步调整为单位元素为4KB大小的数组。
上面这个4KB数组元素的下标,我们就称之为磁盘的LBA地址(Logical Block Address),将LBA地址转换为sector数组下标的方法,如下:
- 4KB / 512Byte(一个扇区的大小) = 8
- n(4KB数组下标)/ [0,1,2,…,7] = sector数组下标
4.2 磁盘空间管理的"分治"思想
经过上面的重重转化,我们已经将原来的硬件设备磁盘空间用这样一个4KB数组描述出来了,于是对于磁盘空间的管理就转化为了对这个4KB数组的管理。由于磁盘空间往往很大,所以在实际的管理中,会将这个4KB数组分成大小不等的多个块去分别管理。这一过分被称之为分区,这样只要管理好一个分区,其他分区只要照搬管理方法就可以很好的管理好整个磁盘空间了。即使只有一片磁盘,也可以进行分区。
在分区之后,可以看到每个区的空间大小仍旧很大,过大的空间管理起来的难度也很大,所以操作系统会在分区的基础上,对于每个分割出来的区域再次划分,这个再次划分的过程我们称之为分组。此时,再针对分割出来的每个组进行具体详细的管理,然后将管理方法移植到其他分组中,从而实现对整个4KB数组(磁盘空间)的管理。这中不断细分,将大问题转换分割为小问题的思想,被称之为分治。
4.3 块组结构与磁盘文件的存储方式
经过上面分区分组的一步步划分,就得到了我们完整的逻辑磁盘空间,一个个块组(Block Group)首位相接。在所有块组的最前端存在着一个特殊的数据块,引导块(Boot Block),它只出现在0号盘面的初始最前端,其主要负责引导开机,在开机的操作中,最重要的就是加载操作系统,而操作系统就存放在磁盘中,所以开机要进行的第一步操作就是先识别磁盘,Boot Block中就存放着加载操作系统的相关程序。
磁盘上存储的是计算机中一个个未打开的文件,而文件由两部分组成,即文件属性和文件内容。所以,存储文件的问题就转换为了如何存储文件属性和文件内容的问题。
我们已知文件的属性类型是确定,并且每一项属性信息会局限在一个可控可知的大小范围内,因此,我们就可以认为文件的属性大小是固定的。而文件的内容则是不可控的,不同文件的内容大小之间跨度区别很大,因此,我们就可以认为文件的内容大小是不固定的。
由于上述文件内容与属性之间特性的差别,所以,在存储时会将两者在磁盘中分开存储。在正式开始学习文件存储方式之前,我们先来了解一下块组的具体结构,以及相应的一些概念。
- Super Block(超级块):负责记录整个分区内各个块组的使用情况(分区总大小,块组的大小,inode数量,空闲块等)。这个数据块理论上一个分区内有一个就足够了,每个块组都配置一个会造成数据冗余,但这个块中的数据格外重要,只有一份的话当数据受损时,整个分区就会瘫痪,导致无法运行,所以,会在后面抽出几个块组添加此块,进行数据备份,后续数据受损时就可进行修复。
- Group Descriptor Table(块组描述符表):负责记录整个块组的使用情况(块组内的区域划分,其实inode编号,块组中有多少个inode,快组中inode的使用情况等)。
- Inode Bitmap(inode位图):以位图映射的方式记录Inode Table中的空间使用情况。
- Inode Table:记录各个文件的in文件属性(inode属性)。
- Block Bitmap(块位图):以位图映射的方式记录Data blocks中的空间使用情况。
- Data Blocks(数据块):记录各个文件的文件内容。
应用层面上,我们是采用路径加文件名的方式来实现对文件的查找与定位的,在系统层面上,则没有文件名的概念,而是每个文件都有一个自己的专属的唯一编号(inode编号),系统就是依靠inode编号来实现对文件进行命名与查找。使用ll -il
命令即可查看到每一个文件的inode编号。文件属性存放在inode Table中,文件内容存放在data blocks中。
除开inode Table与Data Blocks的其他数据块都不属于文件本身,而是属于文件系统的管理字段。我们对电脑分区(磁盘)的格式化,就是向指定分区写入新的管理字段(Super Block,Group Descriptor Table),写入全新文件系统的过程。
为什么现如今多种兴起的编程语言都采用了面向对象的设计方式,若这只是某几种语言的个例特性那倒不足为奇,可现在大部分常用编程语言都采用了这种设计方式,这就证明了这种设计方式的科学性。人类去认识所有的事物都是通过描述这个事物的有限个重点属性这种方式去完成的,而这一点也就面向对象的核心思想,所以,由此可得知,面向对象的设计方式不是语言级的特点,而是我们去认识世界的客观方法论。
以下细节性的设计方案与知识都是基于Ext*(2)文件系统的背景下,操作系统采用了定义struct inode
这一内核数据结构的方式来对文件的属性进行描述和管理。struct inode中记文件的各种基本属性,在Ext*(2)文件系统中,struct inode的大小为128Byte。磁盘上的块组中Inode Table内部就存储着这样的一个个struct inode类型的对象。
每个文件的struct inode中,都有一个很重要的属性int block[15]
,这个数组中就存储着此文件内容存放处的data block编号。通过这一数组就实现了文件属性与文件内容的映射,一个data blocks编号对应着一块4KB大小的数据块(操作系统IO的基本单位)。
inode Bitmap中的每一个二进制位都映射着inode Table中的一个struct inode对应,同样的,block Bitmap中的每一个二进制位也都映射着data Blocks中的一块4KB的大小的数据块。位图中的二进制为0时则代表相对应资源未被占用,为1则代表被占用。
文件的inode编号在整个分区内是唯一的,但在各个分组之间的维度上是不唯一的,这是因为inode编号在分组内是以偏移量的形式存在,即inode Bitmap中的二进制位的相对位置。分区上的inode编号是块组inode起始量 + inode组内偏移量
。每个块组的inode起始编号都存储在Group Descriptor Table中。每个块组中的inode编号数量与块组数量可能出现,一方用完一方没用完的情况,不过大多数情况下不会发生,在初始设计时就采用了专门的配比算法去设置两者数量比例。
data blocks中的一个数据块大小只有4KB,在struct inode中只为每个文件配备了15个4KB大小的数据块(15 × 4096 = 61,440Byte),可是在日常的使用中上百兆,甚至几个G大小的文件也是存在的,这显然不能满足使用要求,那么,这里是如何去设计解决这个问题呢?
操作系统在设计时,这里采用了多级索引的方法,这样就可以大大增加文件的存储容量。
- [0,11]:直接写入数据。(12 × 4KB)
- [12,13]:不写入数据进行二级索引,写入其他数据块的编号。(4KB × 4KB)
- [14]:进行三级索引,连续两次映射,存入其他数据快的编号,而后再进行数据写入。(4KB × 4KB × 4KB)
经过上面的层层逻辑抽象,操作系统对于文件系统的管理就转换为了操作系统对Super Block这个数据块的管理。一台计算机会有多个分区,而每个分区都可以有不同的文件系统,当OS需要管理对应的文件系统是,只需要将Super Block加载(load)到内存中,将不同文件系统的Super Block以链表的方式串联管理起来,这样OS对整个磁盘空间多个文件系统的管理,就变成了对这个Super Block链表增删改查的操作,这里再一次很好的体现了操作系统对计算机软硬件资源"先描述,在组织"的管理理念。
4.4 再次理解文件操作
-
文件名,路径与inode
操作系统的设计理念中,一切皆文件,所以,目录本身也是一种文件,既然是文件,那么,目录就会有它自己的文件属性和文件内容。目录属性中的权限(r:读,w:写,x:可执行),分别表现为r
是指是否拥有访问目录中文件信息的权限,w
是指是否拥有在目录中增删改文件的权限,x
是指是否拥有可以进入目录的权限。
用户在应用层面上对文件的定位是通过文件名和路径实现的,文件名的标识方式更便于用户去浏览使用,可是,在系统层面上对于文件的管理则是通过inode编号实现,应用上与系统层面上对于文件的标识方式是不同的,那么这里是如何将两者间打通联系起来呢,操作系统是通过在目录文件的内容中记录当前目录下各个文件名(字符串)与其inode编号的映射关系来实现。
想要获得文件的inode编号,就要找到文件名所在的目录,通过目录中记录的文件与inode的映射关系得到目标inode编号。可是,文件所在目录的inode编号我们又要从哪里获得呢,目录也是一种文件,其文件名与inode的映射关系同样存储在父级目录中,那么,问题就转换为了不断向上寻求父级目录inode编号的重复问题。操作系统在开机启动时会将根目录( \ )的inode常驻在内存中,在查找文件inode不断向上寻求父级inode编号的最后都会以获取到根目录的inode为结束。所以,我们每次查找一个文件,在内核中都要逆向递归般得到根目录的inode。
操作系统不会初始就去创建并维护整个目录结构的细节,而是会将已经打开的目录进行维护,打开多少路径就缓存多少路径,对路径的缓存是通过形成路径树的方式实现的。从根目录进行路径解析,磁盘中各种文件(视频文件,音频文件,目录)都没有区别,而对于文件类型的区分,是通过在内存中的struct dentry
类型数据结构,一个文件对应一个struct dentry
对象,这个数据结构中记录了文件的inode属性与路径属性(包含权限、所有权、时间戳、文件类型)。 -
再次理解文件操作
有了上面对于块组结构,文件名,inode的了解,此时我们再来重新学习各种磁盘文件的文件操作。 -
新建文件:确认哪个块组中有空闲inode后,先看块组中的inode位图,添加inode,而后再为这个文件的struct inode分配对应的data block,最后再于所在目录文件的内容中添加映射关系。
-
删除文件:删除磁盘文件时,不会对被删除文件的内容做清空,而是直接将其再inode Bitmap中对应的二进制标志位置零,标识无效,最后删除目录文件中的映射关系,此种删除管理数据的方式,非常高效。
-
访问文件(查看/编辑):在正式访问文件前,不会直接去对应的struct inode处访问对应的文件资源,而是会遍历一遍inode Bitmap查找到对应的标识位,确认这个文件确实存在。
-
inode只在分区内有效,如何确定文件在哪个分区
获取每个文件的inode都先要逆向递归到根目录的inode,可是,根目录的inode是如何与根目录本身建立起联系来的呢。一个被写入文件系统的分区,Linux要使用,需要先将具有文件系统的分区进行"挂载"(挂载命令:mount
命令),这一过程就是将目录结构与文件系统创建联系。
文件的inode编号在整个分区内唯一,但是,一台计算机可能拥有多个分区,我们如何确定文件在哪个分区中呢。经过操作系统对文件系统分区的挂载操作,我们就可以通过文件的路径前缀判断文件所属哪个分区。由此,我们访问一个文件,其中需要经过的判断分区,判断分组,从文件名到inode,从抽象数据结构到硬件设备的读写就全部贯通起来了。
系统命令查看分区df -h
,ls /dev/vda*
5. 软硬链接
5.1 软硬链接的指令
- 软连接指令:
ln -s 目标文件 生成软连接名称
,ln(link缩写)
- 硬连接指令:
ln 目标文件 生成硬连接名称
5.2 软硬连接的原理
- 软链接创建的是一个文件,这个文件中存储着目标文件的的路径,拥有独属于自己的inode编号。
- 硬链接不是一个独立的文件,我们可以将其认为是一个指向目标文件的指针,对目标文件的重命名,本质上是向所处目录文件中多添加了一对与目标文件inode的映射关系,inode与目标文件相同。
- 只有文件的引用计数归零,才会真正进行文件的删除操作。
- 上图中,第三列的数据指的是当前文件的引用计数(硬链接数),即存在多少对文件名与inode的映射关系。删除软硬链接的方式除开
rm
命令外,也可以采用unlink
命令。
5.3 软硬连接的应用场景
- 软连接:大型项目快速找到目标文件(与windows中的快捷方式功能相同)
- 硬链接:当前目录
.
,上级目录..
当我们新建一个目录文件时,会发现它的默认引用计数为2,这是因为在创建目录时,操作系统会默认生成.
,其与当前目录文件的inode编号映射。(ls -d
:显示出目录本身的信息)
在新建的目录empty中再创建一个新目录a,发现empty目录的引用计数变为了3,这是因为新建目录a中也默认生成了一个映射向empty目录inode的硬链接..
。
由此,我们可以得出一个结论,一个目录下有多少个子目录,可以通过其硬链接数 - 2计算得到。
操作系统不允许用户自己对目录进行硬链接,因为这可能导致环路问题,使得工具(如,find,rm -rf)遍历目录时会发生无穷递归,除非系统自己建立,系统在自己建立时会进行特殊处理。