大山之二:文件系统(Ext系列)

前言:

被打开的文件在操作系统中被管理起来,没有被打开的文件是存储在磁盘当中

上次讨论的是打开的文件(基础IO部分讨论,即:内存级文件),那没有被打开的文件(磁盘级文件)在哪里呢?又如何保存,并且快速找到某个文件呢?

文件是一种目录结构,而目录结构是树状的。定位到某一个文件需要路径(绝对/相对路径)

文章目录

一、理解硬件

1. 磁盘的存储结构

磁盘是:外设,读写速度慢,但是容量大,价格便宜。

  1. 磁盘的存储单元是扇区(512字节),这意味着:若OS想修改这个磁盘上,某个扇区的一个bite位,都需要将整个扇区的内容加载到内存,将一个bite由0置1,然后再写回到指定扇区

  2. 一个磁盘有3个片,每个片分上下两面,共6面

    磁道并不是连续的,而是由多个扇区间隔组合而成的

(1)磁头刚开始动---->先确定 对应的磁道/柱面Cylinder;

(2)磁头不动,盘片转---->再定位 扇区sector

(3)最后选择哪个磁头 head---->选择哪一面

【只要有柱面,磁头,扇区,就可以定位磁盘上的任意一个扇区,进行读和写】Cylinder, head, sector:CHS定址法

  1. 盘片的转速越高(确定扇区越快),磁头臂摆动的频率越高(确定磁道越快),效率就越高
  2. 磁盘容量=磁头数×磁道(柱面)数×每道扇区数×每扇区字节数

2.磁盘的逻辑结构

  1. 磁盘的逻辑存储结构:LBA

    磁盘比作磁带,将磁带拉直,形成线性结构

    可以将磁盘看成一个黑盒子,给它一个LBA地址,我们就可以向磁盘中特定的扇区里写入【OS没有必要记录每一个扇区的LBA地址,只需要知道磁盘总容量即可。总容量知道,扇区的大小知道,那任意扇区的地址就都知道】

  2. 某一盘面的某一个磁道展开:即:一维数组

柱面是一个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。

柱面:整个磁盘所有盘面的同一个磁道,即柱面展开:(柱面上的每个磁道,扇区个数是一样的,这不就是二维数组吗)

整盘:n个柱面:多张⼆维的扇区数组表

先找到哪一个柱面(Cylinder) ,在确定柱面内哪一个磁道(其实就是磁头位置,Head),在确定扇区(Sector),所以就有了 CHS 。

每一个扇区都有一个下标,我们叫做 LBA(Logical Block Address) 地址,其实就是线性地址。

同一个柱面,磁道的半径相同
OS只需要使用LBA就可以了!!

由磁盘自己来进行:LBA地址转成CHS地址,CHS如何转换成为LBA地址。

  1. 地址转换
    LBA转成CHS :(LBA就是刚刚很长的数组的下标)
    • 柱面号C = LBA // 单个柱面的扇区总数(总数=磁头数* 每磁道扇区数)
    • 磁头号H = LBA %每磁道扇区数 (每磁道扇区数=磁头数* 每磁道扇区数)
    • 扇区号S = (LBA % 每磁道扇区数) + 1
    CHS转成LBA
    • 磁头数* 每磁道扇区数 = 单个柱面的扇区总数
    • LBA = 柱面号C* 单个柱面的扇区总数 + 磁头号H* 每磁道扇区数 + 扇区号S - 1

磁盘使用者/操作系统,根本就不关心CHS地址,而是直接使用LBA地址,磁盘内部自己转换(内部进行:磁盘在真实寻址时,需要将LBA地址转换成CHS地址,然后再去寻址)。所以:从现在开始,磁盘就是一个 数组元素为扇区 的一维数组,数组的下标就是每一个扇区的LBA地址。OS使用磁盘,就可以用一个数字访问磁盘扇区了。

二、文件系统

其实硬盘是典型的"块"设备,操作系统读取硬盘数据的时候,其实是不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。

OS文件系统访问磁盘,不以扇区为单位,而是以"块"为单位,一般4KB(连续8个扇区)

文件系统使用磁盘 ,是以4KB 为基本单位。整个容量÷4KB=多少个块

(存数据的时候也是以4KB为单位存储的)

管几百GB的分区 --->分成n个小区---->每个区又分成m个---->每个组里面包含...


分区是承载文件系统的容器,文件系统只能跑在分区里,硬盘本身不能直接当文件系统用。

  1. 任何文件,都会有同样类型的属性。

    每一个文件的属性内容不一样,但每一个文件(都有的,确定的)属性放在一个struct inode结构体里,值可以不一样,但所占空间大小是一样的128字节 --->Linux中,任何文件都要有自己的属性集合

  2. 每一个文件的属性,都是要从内存写入磁盘的。

    原因:内存断电就清空,而把文件属性从内存写入磁盘,就能长期固化保存。文件系统要靠这些属性找文件文件的位置、层级、权限这些属性,存在磁盘上的文件系统表里,操作系统才能顺着目录、路径找到文件、管理文件。

  3. 一个文件的核心属性全部保存在 struct inode 结构体中 ,每个文件的属性大小都是一样的

    (1)文件名(有长有短)不会作为属性保存在inode中

  4. 硬盘就是一个典型的块设备(硬盘的每个分区,都会被系统划分成一个个大小固定的存储块),块是文件系统读取文件的最小单位

  5. 任何文件的属性都是inode结构体对象(任何文件的属性大小都是一样的)

一个数据块4KB(4096字节),而inode是128字节,所以一个数据块,会保存32个inode

  1. 文件名不会作为属性保存至inode,因为文件名这个字符串大小不固定,会破坏 inode 结构。通过编号int inode_number来辨别文件,区分文件的唯一性(ls -i选项即可查询编号) 【通过编号即可找到文件的所有内容】

  2. 那文件名保存在哪里呢?

磁盘底层本身没有严格的目录、文件区分,目录本质上也是一个文件,和普通文件一样,都由文件内容 + inode 属性两部分组成,所以目录的保存方式和文件一样

文件名不存放在 inode 中,而是保存在它所属目录的【文件内容】里。

【目录这种文件的数据块中,存放的是:一条条目录项,记录着文件名与 inode 编号的映射关系】inode的映射关系也是数据,保存在目录的数据块中

文件查找完整逻辑

查找 Linux 下任意文件,都要从根目录开始逐级路径解析:

先逐层打开路径中的各级目录,读取对应目录的文件内容,得到文件名和inode的映射关系。通过文件名查找到inode编号--->再依靠 inode 中的属性与数据块指针,最终定位到文件真正的内容。

  1. 如何通过inode值来找到内容:先根据除运算知道是哪一个组;再根据inode值去inode Bitmap去查询状态,如果是1则说明这个值是合法的。再去inodeTable拿到属性。那如何知道数据块(存放内容)是哪些呢?在struct inode中有一个大小固定的数组int datablocks[N]:是该文件所对应的数据块编号,通过数组的映射即可知道此文件所有的数据块

了解:block是块,存文件内容;inode存属性

  1. 在分组当中,都以4kB为基本单位

    磁盘分区在进行空间划分与文件存储分组时,普遍以 4KB 作为基本块大小、基本分配单位。系统会把整个分区统一划分成一个个大小固定为 4KB 的存储块,无论是存放文件内容、目录数据,还是文件系统管理空间,都以 4KB 块为最小操作和分配单位。即使是很小的文件,不足 4KB 也会独占一整个 4KB 块;读取、写入文件时,操作系统也不会按字节操作,而是一次性以 4KB 整块为单位进行读写,既方便文件系统统一管理磁盘空间,也能提升磁盘读写效率。

  2. 块位图 就是记录哪个数据块被用/没被使用

    假设data blocks有10万个4KB,如果要新建文件,就需要在data blocks中选择若干个没有用的blok去用。如何知道哪些block没被用?需要通过block bitmap查询(data blocks有10万个4KB,块位图就有10万比特位,如果这个block已使用,则置1;没使用则清零)

  3. 每一个分组有多少inode就会决定inode Bitmap多大,每一个组有多少数据块就会决定Block Bitmap多大

  4. inode Bit map同理

  5. GDT(块组描述符表)用于整体宏观描述,它记录了描述当前块组的所有信息(从哪里到哪里是inode Table,当前有多少inode和数据块还没有被占用等等)

  6. Super block:超级块,一个分区的整体情况:第一个分组,第二个分组从哪里开始,到哪里结束。记录的信息还有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息

不是每一个分组都带有super block,而是个别分组带有super block,且每一个都完全一样(原因:如果super block出问题,这个文件系统就会出问题,所以一个super block出问题,还有块组里有备份,可以进行恢复

(为了保证文件系统在磁盘部分扇区出现物理问题的情况下还能正常工作,一个文件系统的super block会在多个block group中进行备份,这些super block区域的数据保持一致)

  1. 创建并使用磁盘分组时,需要把超级块(Super Block) 和 块组描述符表(GDT) 的信息写入对应分组。

此时还没有任何文件产生,所以块位图、inode 位图初始都置为 0。

整个写入配置的过程就是格式化,格式化的本质,就是向分区写入并初始化文件系统的各类管理信息。

  1. 硬盘本身只是裸存储硬件,裸硬盘不能直接存储文件。想在硬盘中存储文件,必须先将分区格式化为指定类型的文件系统;文件系统专门负责管理硬盘的文件存储、目录结构与磁盘空间分配。

  2. inode和数据块,可以跨组编号,但不能跨分区(在同一分区,编号唯一,inode 编号只在当前分区内部全局唯一。不同分区 允许出现相同的 inode 号,互不干扰),我们就可以在一个分区上定义出来全局的inode和数据块

那如果知道了inode,那怎么知道它在哪个分区,每个分区都有可能有这个数字:

系统先靠路径锁定分区,在该分区内部通过 inode 编号查找 inode 表,进而定位文件属性与数据块

  1. OS在开机的时候,是否要对磁盘上的所有的文件系统进行管理呢?要

操作系统开机时,需要对磁盘上所有已挂载的文件系统统一进行管理。

系统会在内存中为每个分区申请一个超级块(Super Block)对象,再把磁盘上对应分区的超级块信息拷贝到内存并完成初始化。

有多少个文件系统分区,内存中就会生成多少个超级块对象,然后把这些超级块对象用链表组织起来统一管理。

操作系统对文件系统的管理,本质就是对超级块链表进行增、删、查、改操作。

由于用户随时可能对任意分区、任意块组发起读写请求,操作系统必须全程在内存中维护好各分区的超级块信息,才能快速响应文件存取、空间分配与路径解析等操作。

  1. 拿一个文件的inode,可以找到文件的所有信息

    先去组里看inode Bitmap里看是否为1(这个inode是否合法),再去inode table去得到所有的属性。

    如何找到数据在哪些块里面?在inode Table里有一个int datablock[N]该文件的数据块编号

  2. 当读取一个文件时,(磁盘上的文件系统只认inode):先打开当前所在路径,读取对应目录里的数据内容得到文件名和inode的映射关系,得到indoe之后进行文件的查找

  3. 在Linux中,所有的动作都会被转为进程:文件路径是进程提供

  4. 磁盘里没有目录,只是在内存中构建了路径的二叉树

  5. 已经知道如何根据目录文件内容找inode(对目录进行解析得到文件名,就找到它的inode)也知道如何根据inode在指定分区找文件,那怎知道在哪个分区呢?毕竟不能跨区编号

(对磁盘进行分区,对分区进行格式化,到这里仍然不能直接使用分区。分区一定是要和一个目录进行关联,通过进入这个目录,就相当于进入某个分区------>挂载)

在做路径解析的时候,路径前缀。如果文件存在,一定是属于某个分区(分区一定是被挂载到某个路径下的,所以可以通过该路径的前缀,根据就近原则就能知道该分区在哪个路径下

使用open打开文件时,已经指明了文件所对应的路径,接下来OS对路径进行解析(从根目录解析/查找多叉树)打开当前文件所对应的目录,找该文件,目标文件和inode的映射关系,找到该文件的inode,再拿着inode编号去磁盘的特定分区,特定分组里找到所有内容

  1. 通过文件描述符找到文件,struct file里有文件对应的struct dentry,在里面又能找到inode

软硬连接

软连接

本质:是一个独立的文件,有自己的inod_number

软连接以L开头,证明自己是一个链接文件

code-soft这个文件,是 code.c 的软链接(也就是快捷方式)
->是 Linux 中表示指向关系的符号,说明前面的链接指向后面的真实文件。

软连接就相当于windows下的快捷方式,快捷方式是一个独立的文件,删除快捷方式并不影响原来的文件。有快捷方式的目的:让用户快速找到目标文件/程序

软连接的内容:保存目标文件的路径!可以对普通文件建立,也可以对目录进行建立

硬链接

硬链接:本质不是独立的文件,他没有单独的inode

硬链接 = 给同一个 inode,再多起一个新文件名

本质:一组新的文件名和目标inode_number的映射关系

硬链接:只能对普通文件进行建立(Linux中,用户不能对目录进行硬链接,用户对目录进行硬链接的话,容易形成路径环问题)

  1. ...本身都表示一个硬链接

  2. 硬链接用途:

    (1)对文件内容进行备份,防止文件误删;

    (2)不复制数据,节省磁盘空间;

    (3)保留文件本体,避免误删丢失;

  3. 为什么要把磁盘的内存进行缓存呢?

    对文件进行操作时,只能在内存中对文件进行操作。先将磁盘的文件数据加载到内存,然后进行增删查改,再刷新到外设

磁盘是机械设备,向磁盘中进行读写的速度太慢了,会导致IO效率特别低

相关推荐
打工人小夏1 小时前
使用finalshell在新服务器上部署前端页面
linux·服务器·前端·vue.js
少司府1 小时前
C++基础入门:vector深度解析(七千字深度剖析)
c语言·开发语言·数据结构·c++·容器·vector·顺序表
Zhu7581 小时前
软件更新-openssh和openssl-centos
linux·运维·centos
yqcoder1 小时前
突破性能瓶颈:深入理解 JavaScript TypedArray
java·开发语言·javascript
故事还在继续吗1 小时前
嵌入式Linux基础知识
linux·运维·服务器
yqcoder1 小时前
JS 中的“空”之双雄:null vs undefined
开发语言·前端·javascript
ch.ju1 小时前
Java Programming Chapter 3——Traversal of array
java·开发语言
计算机安禾1 小时前
【c++面向对象编程】第8篇:const成员与mutable:常对象与常函数
开发语言·javascript·c++
idolao2 小时前
CentOS 7 安装 httpd-2.4.1.tar.gz 详细步骤(源码编译、配置、启动)
linux·运维·centos