前言
我们之前阐述的内容都是在文件打开的前提下, 但是事实上不是所有文件都是被打开的, 且大部分文件都不是被打开的(也就是文件当前并不需要被访问), 都在磁盘中进行保存. 那这些没有被(进程)打开的文件, 也是需要被管理的! 对于这部分文件核心工作之一是能够快速定位文件, 需要打开的时候能立即打开.
所以文件的管理工作分为: 对打开的文件进行管理, 对没有被打开的文件进行管理, 这些都属于文件系统的范畴, 文件系统解决的是文件存储问题.
而文件 = 属性 + 内容, 文件存储的问题也就是对 文件的属性 + 内容 存储的问题, 我们把文件存储在磁盘中, 至少需要方便 "我们" OS 或者 用户 进行增删查改.
磁盘对应的是硬件存储结构, 对磁盘进行一个逻辑抽象, 称为逻辑存储结构, 什么是逻辑抽象?
操作系统需要通过软件的方式对底层的硬件差异化进行屏蔽, 这种动作就叫做逻辑抽象.
为什么需要逻辑抽象?
OS无法直接去访问硬件, OS需要一个逻辑抽象为其提供一个管理硬件的统一视角.
磁盘
1.认识磁盘
磁盘分为硬盘与软盘, 其中软盘是计算机最早的可移介质, 曾经盛极一时, 但是软盘容量小, 寿命短,由于U盘的出现, 软盘的应用逐渐衰落直至淘汰.
而硬盘是一个机械结构加外设的组合体, 容量很大, 运行速度也很快, 但是快慢是相对的, 机械结构即使再快相比于光速级别运动的电子还是慢了太多, 所以硬盘的访问速度相比于内存和CPU是非常慢的. 而硬盘中还分为机械硬盘(HDD)和固态硬盘(SSD), 它们的工作方式不同.
固态硬盘与机械硬盘
现在笔记本电脑中更多是选择固态硬盘SSD. 因为SSD使用闪存芯片作为存储介质,没有机械运动部件,数据存储和读取通过电子信号完成,因此速度快、抗震抗摔、低噪音、低功耗;HDD采用传统的旋转式磁盘, 通过磁头在磁盘表面移动来读写数据, 因此存在噪音和震动, 防震抗摔性相较固态硬盘较差.
机械硬盘:
固态硬盘:
虽然固态硬盘优势不小, 但是依旧不能取代传统机械硬盘:
SSD的容量通常较小, 价格较高, 适用于对性能要求较高、需要快速读写速度的场景, 如高端计算机、移动设备和游戏机; HDD适用于需要大容量存储空间、对成本敏感的场景, 如服务器和备份存储, 机房里一般都是机械硬盘.
由于传统机械硬盘与固态硬盘都各有所长, 各有所短, 在很多时候人们也会选择混盘就是磁盘和固态硬盘混用.
2. 机械硬盘的物理结构
硬盘的结构可分为外部结构和内部结构.
(1) 外部结构
硬盘外部会有一个金属的面板, 金属面板与其外壳封存着硬盘的内部结构, 用于保护整个硬盘.
在金属板的背面, 我们能看到它控制内部结构的电路板(这一部分都是暴露在外的), 该电路板上的电子元器件大多采用贴片式元件焊接, 这些电子元器件组成了功能不同的电子电路, 这些电路包括主轴调速电路、磁头驱动与伺服定位电路、读写电路、控制与接口电路等.
(2) 内部结构
硬盘的内部结构专指盘体的内部结构, 它是一个密封的腔体, 里面密封着磁头, 盘片等部件.
1. 硬盘的盘片是硬质磁性合金盘片, 它的两面都可以通过磁效应储存数据, 所以对应的磁头也是上下各一个. 硬盘的盘片一般不止一张, 这些盘片安装在主轴电机的转轴上, 在主轴电机的带动下高速旋转. 每张盘片的容量称为单碟容量, 而硬盘的容量就是所有盘片容量的总和. 早期硬盘单碟容量低, 所以, 盘片较多, 而现代硬盘的盘片一般只有少数几片.
2. 一块硬盘内的所有盘片都是完全一样的, 使用不同数量的盘片, 也就实现了一个系列不同容量的硬盘, 磁头摆动和盘片旋转是通过马达控制的, 我们可以通过硬件电路组成伺服系统给磁盘发二进制指令, 然后让磁盘定位去寻址某个特定的区域, 然后从磁盘上读取数据.
3. 磁头和盘片是不接触的, 它们的间距大概在0.1微米到0.3微米之间.(一根头发的直径为几十微米) 打个比方就像一架波音747在离地面1米的距离飞行, 真正的刀尖上跳舞.
4. 磁盘是不能沾灰尘的. 因为磁头与盘面的距离过于微小且盘片旋转速度极快, 所以磁盘必须防抖动, 一旦磁盘沾上灰尘, 即使是我们常说的PM2.5, 其直径也有2.5微米. 一旦落灰,磁头和盘面接触就会使得盘面刮花, 丢失数据. 所以一旦磁盘的内部结构被打开, 这个磁盘就直接报废了, 所以磁盘的组装一般都会在特定的高度无尘环境中.
磁道, 扇区和CHS定位法
磁道是盘面上的半径不同的同心圆环, 而每一个磁道按角度被分为几个相同的圆弧叫做扇区, 扇区是磁头寻址的基本单位, 每个扇区存储都是512byte/4kb, 虽然越靠近圆心外侧面积越大, 但是可以通过工艺进行不同扇区的电子密度大小进行调解, 使得每个扇区的存储大小相同.
一个盘面可以有很多的同心磁道, 一圈磁道可以有很多扇形的扇区, 扇区是磁盘的最小存储单元
由于主轴上一般都有多个盘片, 每个盘片上又有两个盘面, 而且所有磁头的运动都是同步的. 也就是说每一个磁头都会同时偏转到自己盘片的相同磁道上, 这些磁道构成一个上下的面, 叫做柱面, 所以定位磁道, 也可以叫作定位柱面.
如果我想对一个扇区进行写入, 我们该如何寻址定位?
1. 选择一个柱面(Cylinder) , 也就是选择好确定半径的磁道
2. 选择好磁头(Head), 也就是确定选择哪一个盘面.
3. 选择该磁道的扇区(Sector).
这种定位方式称为: CHS定位法
我们既然有了CHS, 就可以向特定的扇区去写入, 那我们也就可以向任意一个/连续多个扇区去写入, 也可以随机写入.
磁盘工作视频:
磁盘工作
3. 磁盘的逻辑结构
磁盘在硬件层面给我们提供了一个个扇区去存储数据, 那我们的数据应该放在哪, 为什么放在这, 下次怎么再找到这个数据呢? 这些工作不是硬件能解决的了, 所以就要在硬件之上构建软件, 就需要对磁盘进行软件层面的逻辑抽象, 以方便操作系统管理磁盘.
1)数组化的抽象理解
仔细观察磁带, 它的两个滚轮上缠着一条长长的带子. 如果我们把磁带扯直, 它就是一条细长的黑色矩形长带, 这条长带中存放着数据.
类似的, 如果我们将磁盘的盘面拉直, 也是一个长矩形, 所以就可以把磁盘想象成一个线性的空间, 里面同样存在着数据.
一个盘片都有两面, 盘面怎么区分呢? 假如有一个两片四面的磁盘, 把一整个数组划分为四块即可,
磁道和扇区也是同理:
这样我们将整个磁盘看做一个数组, 可以将不同盘面划分成一块块数据, 把这一块块数据又划分为不同的磁道(同心圆), 这样对磁盘的管理就变成了对数组的管理.
操作系统, 可以按照扇区为单位进行存取, 也可以基于文件系统, 按照文件块为单位进行数据读取. 也就是说我们可以按照一个扇区512byte去访问, 但是由于扇区的数量比较小, 数目众多在寻址时比较困难, 所以操作系统就规定以若干个扇区为一个簇为基本单位, 再对簇进行整体的操作. 比如8个扇区(4kb)为一个簇, 也就是说磁盘要读取8个扇区之后, 才将数据块传给操作系统.
磁盘读写基本单位是扇区, 操作系统是通过块和簇来做为单位读取等操作数据的, 扇区是对硬盘而言,是物理层的; 块和簇是对文件系统而言,是逻辑层的.
LBA地址
每一个这样的逻辑块地址, 叫作LBA地址(Logical Block Address), 每个逻辑块都有一个唯一的LBA地址, 这种抽象使得操作系统不需要关心磁盘的物理结构, 只需要将数据的逻辑地址发送给硬盘控制器, 控制器负责将其映射到相应的物理位置, 简化了操作系统对磁盘的管理
2)访问数据
文件系统
假如我们有一块500GB的磁盘, 但是把500GB空间作为一个整体管理太大了, 现在把500GB拆分成3个100GB和1个200GB, 所以其实把100GB的空间管理好了就可以把其它的100GB都管理好, 这个动作其实叫作分区, 所以我们在电脑上看到的CDEF等磁盘都只是一个范围分区, 最终存储的位置都是一块硬盘(假如电脑没有外装其他硬盘).
100GB的空间又可以划分成一个个group, 不同的系统的group划分方式是不一样的, 假如现在1个group是2GB, 把这100GB分为50个group, 所以管理整个磁盘其实只要把2GB的空间管理好, 利用了分治的思想.
在group之前发现还有一个叫启动块的东西.
一块硬盘由一个启动块和许多个块组组成.
启动块(Boot block)
可以认为它存储在整个磁盘中0号盘面0号磁道第一个扇区, 大小为1kB, 用于存储磁盘的分区信息和系统的启动信息, 任何文件系统都不能使用该块. 其它扇区都可以坏, 一般文件系统有一定的修复能力, 但是如果这个Boot block块损坏, 整个文件系统也就无法启动, 甚至操作系统的启动也不能进行.
块组(Block group)
文件系统会根据分区的大小划分为数个Block Group, 每个Block Group都有着相同的结构, block group从启动块开始编号从0到n. 一个块组 (Block group) 还可以被分为超级块(Super block), 块组描述(Group Descriptors), 数据块位图(Data block Bitmap), inode位图(inode bitmap), inode表(inode table),数据区(Data blocks)
组中管理的数据分为两类, 一类是文件的数据 (内容+属性), 另一类是管理文件的数据, 在正式使用整个分组之前, 需要先让管理文件的数据写入到块中.
icode编号
linux中 使用ll -i 指令, 可以看到每个文件多显示了一列编号, 这个编号就叫inode编号, 一般情况一个文件只有一个inode且每个文件都有.
inode编号在整个分区具有唯一性, linux内核中识别文件, 与文件名无关, 只和inode编号有关.
inode与inode表(inode table)
inode
inode是文件系统中的一种数据结构, 用于存储有关文件或目录的文件属性(如文件大小, 权限, 所有者, 最近修改时间, inode编号等文件属性) 以及指向文件数据块的指针, 但不包括文件的实际内容.
inode是文件系统内部使用的数据结构, 它们对于用户是透明的, 用户只能通过文件名访问文件, 而不需要直接操作inode.
我当前文件系统(ext4)的Inode的大小是256字节, 但在ext2文件系统重inode大小为128字节.
inode表
inode表中保存了分组内部所有可用的(已经使用和未被使用)inode. 它可以看作是一个数组,每个元素都是一个inode, 由于inode的大小和inode表的大小通常是固定的, 所以可以通过下标来访问每个inode. 当我们创建一个文件时, 第一时间找的就是inode Table, 将inode存放在这个表中.
每个Block group都有一个起始的inode编号, 当得到一个inode编号时, 根据 Block group的起始node编号即可确定当前inode编号在哪个Block group里.
数据区(Data blocks)与数据块:
数据块是文件系统中用于存储文件内容的基本单位, 当你创建一个文件并写入数据时, 文件系统会将这些数据存储在一个或多个数据块中. 而Data blocks被分为很多的4KB大小的数据块.
inode中有Data block编号的数组 int blocks[n] , 可以找到当前文件存储在哪些Data block里.
但是对于一个很大的文件, 这些数据块并不止是链表式的查找, 而是类似树形地查找, 比如有一个blocks[15]的数组, 其中0~12直接映射储存文件的内容的块, 而13号块则映射到14号块, 而14块中存储的不是文件内容, 而是块编号列表, 假如一个块编号为4字节, 一个块就能存储4kb/4byte=1000个块编号, 这属于二级映射, 这样文件就被扩大了. 同样的, 也可以有三级映射, 文件就被进一步扩大.
如果有一个更大的文件, 比如20GB. 一个group肯定是存不下的, 那就需要进行跨组存储, 在块中存储使用到的块号即可.
块位图(Block Bitmap)
Block Bitmap中记录着Data Blocks中哪个数据块已经被占用, 哪个数据块没有被占用.
inode位图(inode Bitmap)
每个bit表示一个inode是否空闲可用.
如何创建一个文件?
1. 储存文件属性
当我们创建一个文件时, 操作系统会先在inode bitmap查找一个未被占用的inode编号 (位图对应位置为0), 找到后就在inode table中建立一个新的inode结构体并用刚刚找到的inode编号去初始化编号变量, 还有其他的一些文件属性.
2. 储存文件内容
操作系统根据文件大小确定需要几个内存块, 比如该文件需要存储在三个数据块, 那就在Block bitmap里找到3个未被占用的数据块, 内核缓冲区保存的文件内容将先拷贝到第一块数据, 满了之后接着复制到下一个数据块, 直到全部拷贝完成.
如何删除一个文件?
删除一个文件需要把所有的inode清空, 所有的数据块清空?
不需要, 我们直接根据inode编号把inode bitmap里对应的比特位由1置0, 然后把数据块对应的编号也在block bitmap里把比特位由1置0即可, 所以删除文件只需要改两个位图即可.
这就解释了为什么我们在新建一个文件的时候往往很慢, 但是删除文件的时候却很快!
那我们如果误删了一个文件, 思路上该怎么恢复这个文件呢?
首先是什么都不做, 不要再创建文件避免原来的inode编号被占用, 从而对应的数据被覆盖. 然后如果有被误删的文件的inode编号, 去把inode bitmap对应的0置为1, 找到对应的inode找到其中的数据块编号, 在block bitmap中把对应的0置为1. 此时这个文件就被恢复了
综上, 其实 inode table 和 Data blocks 对应的是文件信息, 而 inode bitmap 和 Data bitmap 对应的是管理文件的信息.
块组描述符(Group Descriptor Table):
Group Descriptor Table 描述块组属性信息, GDT中的每个条目对应一个块组, 它包含了该块组的重要信息, 如Block bitmap, Inode bitmap, Inode table的位置以及与该块组相关的其他元数据信息. 通过读取 Group Descriptor Table, 文件系统可以了解每个块组的结构和属性, 从而能够有效地管理和存储文件系统中的数据. 总而言之GDT是用来管理一个Block group的.
超级块 (Super Block)
Super block 存放文件系统本身的结构信息. 记录的信息主要有: bolck 和 inode的总量, 未使用的block和inode的数量, 一个block和inode的大小, 最近一次挂载的时间, 最近一次写入数据的时间, 最近一次检验磁盘的时间等其他文件系统的相关信息. Super Block的信息被破坏, 可以说整个文件系统结构就被破坏了.
超级块不一定在每一个块组中都存在, 可能在其中的个别块组中有超级块. 它存放的整个分区(每个分区是一个文件系统)的信息, 操作系统需要把分区的文件管理起来, 内核中就会维护一张文件系统列表, 把每个分区的超级块导入内存里, Linux操作系统对磁盘的管理就转化成了内核中对若干个超级快的管理.
为什么超级块不像Boot block一样专门划分一块区域, 而是分布在其中的个别块组中?
一个块组中的GDT如果出问题, 影响的顶多是一个块组, 但是如果超级块出了问题, 整个文件系统就会挂掉. 所以以多副本的形式存储超级块, 就可以保证在超级块出问题时, 能够找到其它块组的超级块副本拷贝过来, 保证文件系统的稳定.
以上的文件系统成为ext2文件系统, ext3, ext4等文件系统在ext2的基础上添加了一些日志, 自动修复等功能, 但是它们的内核是一样的.
总结一下, 如果我们拿到了文件的inode编号, 只需要找到super block 查询组的相关信息, 确定inode编号应该落在哪一个块组里, 然后查GDT, 去找到对应的inode, 进而能找到block, 一个文件就被找到了.
往上层(用户层)看, 用户好像从没用过inode编号, 而是一直用的文件名. 首先我们先明确, 用户只用文件名, 内核只用inode编号, 那么inode和文件名之间一定存在一种映射关系.
如何理解目录?
目录也有自己的inode编号, 也有自己的Data block, 那么目录的数据块存什么呢?
当前目录下直接保存的文件的 文件名与 inode 的映射关系, 这个文件包含普通文件和目录.
所以同一个目录下不能有同名文件, 文件名和inode编号都具有唯一性, 互相作为对方的key.
这就能回答:
为什么目录需要有x权限才能在目录下进行文件的创建, 删除, 修改?
因为这些操作需要向目录里新增, 删除, 修改 映射关系, 也就是在修改目录的内容.
为什么 Linux中, 文件名不属于文件的属性, 为什么inode中不存放文件名?
因为文件名在目录中存储着.
软硬链接
1.软硬链接的建立及特点
建立软链接
语法: ln -s+[目录或文件] + [软连接的名称]
创建指向对应文件的软链接文件, 软链接是一个独立的文件.
建立硬链接
语法:ln + [目录或文件] + [软连接的名称]
硬链接不是一个独立的文件, 它没有独立的inode编号.
2. 软链接与硬链接的区别
软链接:
软链接也称为符号链接(Symbolic Link), 类似于Windows下的快捷方式.
软链接可以指向不存在的文件, 但不能对不存在的文件创建硬链接.
删除源文件后, 软链接仍然存在, 但实际上已经变成了“死链接”.
创建软链接时要使用绝对路径, 相对路径创建的软链接无法访问
软链接不受文件系统限制, 可以指向不同文件系统下的文件或目录
硬链接:
硬链接通过Inode编号进行链接, 所以硬链接的inode编号和被连接的文件inode号相同, 一个文件可以拥有多个硬链接, 这些链接指向同一个inode. 如果其中一个硬链接被删除, 其他链接仍然可以访问文件内容.
图片中标注的那一列数字就代表文件的硬链接数, 可以看到删除前硬链接数为2, 删除后变为1
其实inode里存放着一个引用计数, 硬链接就相当于是指针, 删除了之后inode中的引用计数减1, 直到引用计数为0时文件就被删除.
所以硬链接相当于创建了源文件的副本, 修改硬链接文件会影响源文件的内容.
当我们新建一个文件的时候, 发现文件的硬链接数是1, 也就是它自己.
目录的硬链接
而新建一个目录的时候, 发现目录的硬链接数是2, 一个是dir本身, 另一个是谁?
进入到 dir 目录下, 发现 "." 是dir的一个硬链接, 也就是当前目录, 所以创建一个目录默认硬链接数是2:
如果在dir里再创建一个dir:
硬链接数就变成3, 因为子目录的 ".." 也是一个硬链接.
不能对目录创建硬链接
可以给目录创建一个软链接:
但是不能给目录创建硬链接:
为什么? 比如在执行 find 命令查找的时候, 会逐级目录向下查找, 如果此时遇到一个目录的硬链接, 这个硬链接是当前搜索路径上的一个目录, 那么就会陷入循环查找, 显然不合理.
为什么可以建立软链接? 因为软链接是一个独立的文件, 搜索的时候只会进入目录文件搜索, 不会理会它.
删除软硬链接
使用 unlink + [要删除的链接名] 即可删除链接: