• Linux_文件系统(磁盘角度)&&inode


    前言

    在我们的磁盘上,有大量的文件存储着,这批文件非常的多、杂、乱

    操作系统对这些文件的管理的部分叫做文件系统

    Linux_文件系统(内存)一文中我们了解了文件描述符、重定向、缓冲区等应用级概念,这些都是从内存中看待文件的方式,如果想更全面的了解Linux的文件系统,必须从内存中出来

    了解物理磁盘是什么样的,存储单元是什么样,操作系统如何看待磁盘

    从磁盘中一步步走到内存,把知识从想象变为实体

    磁盘结构铺设

    磁盘的物理结构

    首先我们要知道,磁盘是一个机械设备

    但是目前,我们的笔记本上已经不用磁盘的了,而是SSD(固态硬盘)

    但由于成本低等原因,很多企业还是大量使用磁盘的,而且我们现在的操作系统依然还是以磁盘作为物理模型设计的文件系统

    所以后面都已物理磁盘作为切入带点讲解

    如下是磁盘的拆开图:

    • 如上这一个个盘片的两面存储着二进制数据(我们可以把盘片看成是无数个小磁铁拼到一起,每一小磁铁有N/S极,就可以代表一个二进制位),它们都固定于中间的一根轴上,中轴下方有一个马达,磁盘运转的时候,马达会带动盘片以极高的速率转动

    • 上图Read/Write heads称为磁头,每一个盘面上都配有一个,盘面上的数据就是用它读取的,它们可以自由摆动,读到不同半径的圆环;所有磁头都是连在同一个机械臂上的,因此所有磁头只这能共进退

    • 左侧的Logic board这样的电路板会接收驱动给它发布的磁盘协议,控制磁头的摆动、访问磁盘的某些数据

    因为磁盘的这样的机械式结构,再加之它是外设,所以它的读取相对内存是很慢的

    磁盘的存储结构

    当我们俯视盘面的时候,就可以看到这样一个圆面

    • 磁盘表面被分为许多同心圆,每个同心圆称为一个磁道,每个磁道都有一个编号,最外侧是0;

      其中,因为所有磁头共进退,能同时访问的磁道半径都相同,所以我们把轴上相同半径的多个磁道组成的面称为柱面

    • 每个磁道被划分位若干段(这些段又称扇区),每个扇区的存储容量不一定,由磁盘厂商决定,我们后面以最普适的512字节为例

    磁盘上的存储单位是扇区——512字节

    (注意:仅仅是说磁盘的存储物理单位是512字节,不可分割存储,但并不代表将来只能以512字节为单位进行访问,比如可以一次访问4KB)

    当要读写某个特定的扇区,就可以通过确定柱面(Cylinder)–>盘面(Head)–>**扇区(Sector)**的方式,锁定到一个扇区

    对应的物理过程就是机械臂旋转,使磁头转到对应的柱面–>选择盘面对应的磁头–>磁盘旋转找到对应的扇区

    只要对柱面、盘面、扇区都进行编号,就可以得到一个地址,我们把这个地址称为CHS地址

    磁盘的逻辑抽象结构

    我们把磁盘的一圈圈磁道想象成是一圈圈缠绕起来的磁带,当把磁带铺平绕开,所有盘片的都接到一起

    我们就得到一个线性结构,我们就可以把它看成是一个数组,数组的每一个元素代表一个扇区

    一个500G的磁盘就可以看成是一个sector disk[131 072 000]数组

    对磁盘的管理,就变成了对数组空间的管理,定位一个sector,只需要找到下标就行,我们把这个下标称为LBA(逻辑块地址)

    当我们想向磁盘写入,只需要把LBA转化成CHS地址,就可以找到对应的位置

    转换方式:

    假设我们当前的磁盘有4面,每面20个磁道,每个磁道50个扇区,共4000个扇区,形成的数组有4000个元素,(每面1000个元素)

    此时,要将LBA:3234转换成CHS地址

    H= 3234 / 1000 = 3 ; 3234 %= 1000 -->234

    C = 234 /20 = 11; 234 %= 20 -->14

    S = 14

    逻辑划分:

    • **单位:**上面的数组,每个元素代表的物理空间是512字节,我们把每8个元素看待为一个新的存储单元单元,每个单元就是4KB

      这个4KB也就是操作系统看待磁盘存储空间的基本单元,以4K为基本单位进行IO

      那为什么磁盘的基本单位是512字节,文件系统访问磁盘的基本单位是4KB?

      1. 提高IO效率

      磁盘之所以慢绝大因素取决于寻找 ,如果一次访问磁盘就能访问到8个扇区的内容效率可以说提高了8倍

      1. 不要让软件设计(OS)和硬件(磁盘)具有强相关性,尽量让它们在功能上解耦合

      否则一旦硬件变了,操作系统就要进行大改动,把他们分开更有利于两者的发展

    • 分区:一个500GB的磁盘空间,以4K为单位划分,依然太多,太难管理了,所以我们再进行划分,把他划分为几个区域,这就是磁盘分区的过程(相当于将一个国家划分为几个省)

    • **分组:**对100G管理依然很难,我们再把100G划分为10个10G(把省拆分成市)

      因为管理方法是可以共同使用的,只需要能把一个10G管理好,100G也就都管好了,能管好100G,整个500G通过方法的拷贝也能管理好

      注意:我们上面画的这些数组并不是说真的要在物理内存中开一个对应的空间,仅仅是对磁盘这500GB空间的划分与看待。

    Linux文件系统

    前面,我们将一个500GB的磁盘的空间分区为到100GB,又把100GB分组到10GB

    如下就是Linux对一个区(上)和对一个组(下)的管理

    对一个区的管理

    Boot Block:

    当计算机已启动的时候,首先首先要读取Boot Block,这里里面放了计算机的开机信息(分区表,操作系统软件位置),从而启动操作系统

    Block group:

    剩下的n个Block group就是n个分组

    对一个组的管理

    我们知道,一个文件分内容数据两部分,它们都要存储,Linux采用的是将内容和属性分开存储的方案。

    内容放到block中(大小4KB),属性放在inode中(磁盘上的另一块空间,128字节,一个扇区可以存4个inode)

    内容可以增多,可能用多个block存,属性是定长的的,固定是128字节

    Data Blocks:

    一个组中的大多数空间都是Data Blocks,在这部分空间,以块为单位进行文件内容的保存(大小是4KB)

    inode table:

    以128字节为单位,进行inode的保存(文件的属性)

    inode属性里面有一个inode编号

    一般而言,每一个文件有一个inode,对应一个inode编号

    查看文件的inode

    我们可以使用ll -i指令查看文件的inode编号

    Block Bitmap

    这是一个位图结构,用来标识哪些Data Block是否已经被使用

    假设大小为1000bits,也就可以标识1000个块,

    如果当前是0001 1001,就表示编号为1,4,5号的块已经被占用

    Inode Bitmap

    标识每一个inode块是否被占用

    表征inode的使用情况

    Group Descriptor Table

    用于记录:有多少inode,起始的inode编号,有多少个inode被使用,有多少block被使用,还剩对少,当前Block Group的总大小……

    Super Block

    他会记录整个分区有多少Block Group,每一个Block Group的使用情况,整个分区在磁盘的位置……

    为什么这个描述分区状态的部分会被存到块组中呢?他应该像下面这样存到整个块区的结构中啊

    理论上确实应该这样

    实际Linux选取如上的方式,让部分块组存储整个分区的属性,是一种备份的策略

    防止出现一个Super Block的错误导致整个分区失效的情况出现

    当一个损毁了,还可以拷贝其他块组的

    inode

    在上述组的划分中,inode table存的是一个个128KB的结构体体变量

    这个结构体中存着文件的所有属性和一个数组

    struct inode
    {
        //文件的所有属性
        blocks[15];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    blocks数组

    • [0, 11]:直接保存着该文件block编号,通过这个编号可以索引到对应的块

    • [12, 15]:如果该文件的数据存储块超过了12,前面的数组就存不下了,就要用到后四个

      我们知道,数据块用来存文件的内容,但是如果有一部分文件属性在inode存不下了,可以也可以放到数据块中

      如果inode中的blocks数组的前12个不够存所有块编号,就要放到把剩余的b块编号放到数据块中,再把这个数据块的编号存到bloks的[12, 15]的位置

      通过间接寻址就可以找到对应的块

      当然如果四个块的空间还是不够,可以继续多级索

    文件名

    虽然文件名也是文件的属性,但是文件名并不存在inode中

    Linux下,底层之际是通过inode编号标识文件的

    如果获得了一个文件的inode编号,就获得了文件的所有属性,同时也可以找到文件的内容,但是使用的时候都是通过一个文件名找到对应文件的,那如何通过文件名映射到对应的inode的呢?

    我们mkdir创建的目录也是文件

    是文件,就包括内容和属性,它的属性存在它自己的inode,那它的数据块放什么?

    它里面放着目录中存的那些文件的文件名inode编号的映射关系

    (所以说同一个目录下不允许同名文件的存在)

    当我们要创建一个文件:

    1. 在当前分区找一个块组

    2. 查找块组中的Inode Bitmap ,找一个没有被使用的inode编号,把当前编号由0置1,把当前文件的属性值写入对应的inode

    3. 当前文件为空的时候,可以先将inode中的blocks都置空,

      当文件要写入,系统会自动从Block Bitmap给它找一个没有被使用的块map置1,把要写的数据写入块中,再把块编号写入inode中的blocks数组

    4. 我们创建一个文件,一定是在某个目录下创建的

      于是就可以找到文件所处的目录,根据目录的inode找到目录的Data Block,将文件名和inode编号的映射关系写入这个数据块中

    软硬链接

    软链接

    所谓软连接其实就是Windows中的文件快捷方式

    当我们想执行一个文件(test),但它的路径很深

    就要把整条路径带上

    此时就可以使用ln -s [目标文件名] [软连接名]为 它创建一个软链接

    此时,直接运行软链接文件,就可以自动找到路径下的目的文件而执行

    这就是软链接的作用

    我们使用ll -i指令可以看到对应文件的inode编号:

    可以看到,test文件和它的软连接文件的inode编号并不相同

    所以说,这个软链接文件是一个新的文件,它的内容是什么呢?

    就是后面那串路径

    硬链接

    ln [目标文件名] [硬链接名]

    此时就为test创建了一个硬链接test.hard

    直接执行它与执行test相同

    我们再看一下硬链接文件文件和inode和原文件的区别

    可以看到,它们的inode是相同的

    所以,创建一个硬链接就是给当前目录提供目标文件的硬链接名和inode编号的映射关系

    硬链接数

    当我们创建一个文件的硬链接,可以看到,它的文件属性中有一个字段**+1**了

    这个属性就表示文件的硬链接数

    这里的硬链接数本质上文件inode属性中的一个计数器count,它标识有几个文件名与它建立了映射关系

    ./…

    当我们touch创建一个文件,它的硬链接数是1可以理解,

    但是当我们创建一个目录,它的硬链接数直接就是2

    这是因为创建一个目录的同时,一定会在目录下创建了两个硬链接./..

    因为dir和dir下的.两个文件名指向的是同一个文件,所以dir的硬链接数是2

    dir下的..、d3目录下的.和d3指向的又是同一个文件,所以它们的硬链接数都是3

    我们平时执行当前目录下的一个文件时前面带的./其实就是先通过.找到目录,再 找目录下的文件

    当我们想快速知道一个目录下有多少文件的时候,一般就是硬链接数-2

    删除软硬连接

    可以使用rm删除软硬连接

    但是建议使用unlink

  • 相关阅读:
    为什么 JavaScript 模块中的默认导出很糟糕
    Linux扩展篇之Shell编程三(函数)
    Oracle for Windows安装和配置——Oracle for Windows数据库创建及测试
    构造函数和初始化列表的关系和区别【详解】
    Linux防火墙firewalld(粗糙版)
    【嵌入式linux】Ubuntu 修改用户名
    Spider爬虫入门(发送Get Post请求、携带请求头、Cookie、Session、响应Response、获取二进制数据 、解析Json数据)
    uniapp微信公众号跳转到小程序(是点击微信页面上面的按钮/菜单跳转)
    java毕业设计球迷信息交流论坛源码+lw文档+mybatis+系统+mysql数据库+调试
    Java 21 新特性:Record Patterns
  • 原文地址:https://blog.csdn.net/yue152152/article/details/127827690