道尔柯南头像
【Linux】Ext系列文件系统(磁盘级文件)封面图

【Linux】Ext系列文件系统(磁盘级文件)

在这里插入图片描述


🎬 个人主页:道尔柯南

❄专栏传送门:《C语言》《C++》《Linux操作系统》

昙花一现,却等待了整个白昼;蝉鸣一夏,却蛰伏了好几个四季。

1->计算机硬件

1.1 磁盘

机械磁盘是计算机中唯一的一个机械设备,它容量大,价格便宜
在这里插入图片描述

1.2 磁盘结构

在这里插入图片描述
在这里插入图片描述
扇区:是磁盘存储数据的基本单位,512字节,块设备

如何定位一个扇区呢?

  • 可以先定位磁头(header)

  • 确定磁头要访问哪一个柱面(磁道)(cylinder)

  • 定位一个扇区(sector)

  • CHS地址定位

扇区是从磁盘读出和写入信息的最小单位,通常大小为 512 字节。

磁头(head)数:每个盘片一般有上下两面,分别对应1个磁头,共2个磁头

磁道(track)数:磁道是从盘片外圈往内圈编号0磁道,1磁道…,靠近主轴的同心圆用于停靠磁头,不存储数据

柱面(cylinder)数:磁道构成柱面,数量上等同于磁道个数

扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同

圆盘(platter)数:就是盘片的数量

磁盘容量=磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数

细节:传动臂上的磁头是共进退的(这点比较重要,后面会说明)

柱面(cylinder),磁头(head),扇区(sector),显然可以定位数据了,这就是数据定位(寻址)方式之一,CHS寻址方式。

1.3 磁盘的逻辑

在这里插入图片描述

磁带上面可以存储数据,我们可以把磁带“拉直”,形成线性结构。
在这里插入图片描述那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在一起的磁带,那么磁盘的逻辑存储结构我们也可以类似于:
在这里插入图片描述
这样每一个扇区,就有了一个线性地址(其实就是数组下标),这种地址叫做 LBA
在这里插入图片描述

在这里插入图片描述
柱面是一个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。

所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由“柱面”卷起来的。

某一盘面的某一个磁道展开其实就是一维数组

整个磁盘所有盘面的同一个磁道,即柱面展开,就是二维数组。

整个磁盘不就是多张二维的扇区数组表(三维数组?)

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

我们之前学过C/C++的数组,在我们看来,其实全部都是一维数组:所以,每一个扇区都有一个下标,我们叫做 LBA(Logical Block Address) 地址,其实就是线性地址。所以怎么计算得到这个LBA地址呢?

OS只需要使用LBA就可以了!!LBA地址转成CHS地址,CHS如何转换成为LBA地址。谁做啊??磁盘自己来做!固件(硬件电路,伺服系统)

1.4 CHS && LBA地址

CHS转成LBA:

  • 磁头数*每磁道扇区数 = 单个柱面的扇区总数
  • LBA = 柱面号C单个柱面的扇区总数 + 磁头号H每磁道扇区数 + 扇区号S - 1
  • 即:LBA = 柱面号C(磁头数每磁道扇区数) + 磁头号H*每磁道扇区数 + 扇区号S - 1
  • 扇区号通常是从1开始的,而在LBA中,地址是从0开始的
  • 柱面和磁道都是从0开始编号的
  • 总柱面,磁道个数,扇区总数等信息,在磁盘内部会自动维护,上层开机的时候,会获取到这些参数。

LBA转成CHS:

  • 柱面号C = LBA // (磁头数*每磁道扇区数)【就是单个柱面的扇区总数】

  • 磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数

  • 扇区号S = (LBA % 每磁道扇区数) + 1

  • “//”: 表示除取整

所以:
从此往后,在磁盘使用者看来,根本就不关心CHS地址,而是直接使用LBA地址,磁盘内部自己转换。
所以:
从现在开始,磁盘就是一个元素为扇区的一维数组,数组的下标就是每一个扇区的LBA地址。OS使用磁盘,就可以用一个数字访问磁盘扇区了。

2->文件系统

2.1 "块"概念

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

硬盘的每个分区是被划分为一个个的"块"。一个"块"的大小是由格式化的时候确定的,并且不可以更改,最常见的是4KB,即连续八个扇区组成一个"块"。"块"是文件存取的最小单位。

注意:

  • 磁盘就是一个三维数组,我们把它看待成为一个"一维数组",数组下标就是LBA,每个元素都是扇区
  • 每个扇区都有LBA,那么8个扇区一个块,每一个块的地址我们也能算出来。
  • 知道LBA:块号 = LBA/8
  • 知道块号:LAB=块号*8+n.(n是块内第几个扇区)

2.2 "分区"概念

其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有一块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的一种格式化。但是Linux的设备都是以文件形式存在,那是怎么分区的呢?

柱面是分区的最小单位,我们可以利用参考柱面号码的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号码。此时我们可以将硬盘上的柱面(分区)进行平铺,将其想象成一个大的平面。

📌 注意:
柱面大小一致,扇区个位一致,那么其实只要知道每个分区的起始和结束柱面号,知道每一个柱面多少个扇区,那么该分区多大,其实和解释LBA是多少也就清楚了。

2.3 "inode"概念

之前我们说过 文件=数据+属性,我们使用 ls -l 的时候看到的除了看到文件名,还能看到文件元数据(属性)。Linux中,任何正常文件,都要有自己的属性集合。Linux下,内容和属性是分开存储的!
在这里插入图片描述
每行包含7列:

  • 模式
  • 硬链接数
  • 文件所有者
  • 组
  • 大小
  • 最后修改时间
  • 文件名

ls -l读取存储在磁盘上的文件信息,然后显示出来

其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息
在这里插入图片描述
到这我们要思考一个问题,文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息(属性信息),比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。为了能解释清楚inode,我们需要是深入了解一下文件系统。

📌 注意:

  • Linux下文件的存储是属性和内容分离存储的

  • Linux下,保存文件属性的集合叫做inode,一个文件,一个inode,inode内有一个唯一的标识符,叫做inode号

所以一个文件的属性inode长什么样子呢?

/*
* Structure of an inode on the disk
*/
struct ext2_inode 
{
     __le16 i_mode; /* File mode */
     __le16 i_uid; /* Low 16 bits of Owner Uid */
     __le32 i_size; /* Size in bytes */
     __le32 i_atime; /* Access time */
     __le32 i_ctime; /* Creation time */
     __le32 i_mtime; /* Modification time */
     __le32 i_dtime; /* Deletion Time */
     __le16 i_gid; /* Low 16 bits of Group Id */
     __le16 i_links_count; /* Links count */
     __le32 i_blocks; /* Blocks count */
     __le32 i_flags; /* File flags */
     union 
     {
         struct 
         {
             __le32 l_i_reserved1;
         } linux1;
         struct 
         {
             __le32 h_i_translator;
         } hurd1;
         struct 
         {
             __le32 m_i_reserved1;
         } masix1;
     } osd1; /* OS dependent 1 */
     __le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
     __le32 i_generation; /* File version (for NFS) */
     __le32 i_file_acl; /* File ACL */
     __le32 i_dir_acl; /* Directory ACL */
     __le32 i_faddr; /* Fragment address */
     union 
     {
         struct 
         {
             __u8 l_i_frag; /* Fragment number */
             __u8 l_i_fsize; /* Fragment size */
             __u16 i_pad1;
             __le16 l_i_uid_high; /* these 2 fields */
             __le16 l_i_gid_high; /* were reserved2[0] */
             __u32 l_i_reserved2;
         } linux2;
         struct 
         {
            __u8 h_i_frag; /* Fragment number */
            __u8 h_i_fsize; /* Fragment size */
            __le16 h_i_mode_high;
            __le16 h_i_uid_high;
            __le16 h_i_gid_high;
            __le32 h_i_author;
         } hurd2;
         struct 
         {
            __u8 m_i_frag; /* Fragment number */
            __u8 m_i_fsize; /* Fragment size */
            __u16 m_pad1;
            __u32 m_i_reserved2[2];
         } masix2;
      } osd2; /* OS dependent 2 */
};
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
备注:EXT2_N_BLOCKS = 15

📌再次注意:

  • 文件名属性并未纳入到inode数据结构内部
  • inode的大小一般是128字节或者256,我们后面统一128字节
  • 任何文件的内容大小可以不同,但是属性大小一定是相同的

目前为止,相信大家还有两个问题:

  1. 我们已经知道硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,读取的基本单位是”块“。”块“又是硬盘的每个分区下的结构,难道”块“是随意的在分区上排布的吗?那要怎么找到”块“呢?

  2. 还有就是上面提到的存储文件属性的inode,又是如何放置的呢?

文件系统就是为了组织管理这些的!!

3-> ext2文件系统

3.1 宏观认识

所有的准备⼯作都已经做完,是时候认识下⽂件系统了。**我们想要在硬盘上储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。**在
Linux 系统中,最常⻅的是 ext2 系列的⽂件系统。其早期版本为 ext2,后来⼜发展出 ext3 和 ext4。ext3 和 ext4 虽然对 ext2 进⾏了增强,但是其核⼼设计并没有发⽣变化,我们仍是以较⽼的 ext2 作为演⽰对象。
ext2⽂件系统将整个分区划分成若⼲个同样⼤⼩的块组 (Block Group),如下图所⽰。只要能管理⼀个分区就能管理所有分区,也就能管理所有磁盘⽂件。

在这里插入图片描述
上图中启动块(Boot Block/Sector)的⼤⼩是确定的,为1KB,由PC标准规定,⽤来存储磁盘分区信息和启动信息,任何⽂件系统都不能修改启动块。启动块之后才是ext2⽂件系统的开始。

3.2 Block Group

ext2⽂件系统会根据分区的⼤⼩划分为数个Block Group。⽽每个Block Group都有着相同的结构组成。

3.3 块组内部构成

3.3.1 超级块(Super Block)

存放⽂件系统本⾝的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写
⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。Super Block的信息被破坏,可以说整个⽂件系统结构就被破坏了

超级块在每个块组的开头都有⼀份拷⻉(第⼀个块组必须有,后⾯的块组可以没有)。 为了保证⽂件系统在磁盘部分扇区出现物理问题的情况下还能正常⼯作,就必须保证⽂件系统的super block信息在这种情况下也能正常访问。所以⼀个⽂件系统的super block会在多block group中进⾏备份,这些super block区域的数据保持⼀致。

3.3.2 GDT(Group Descriptor Table)

块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组 的描述信息,如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是DataBlocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷⻉。

3.3.3 块位图(Block Bitmap)

Block Bitmap中记录着Data Block中哪个数据块已经被占⽤,哪个数据块没有被占⽤

3.3.4 inode位图(Inode Bitmap)

每个bit表⽰⼀个inode是否空闲可⽤。

3.3.5 i节点表(Inode Table)

• 存放⽂件属性 如 ⽂件⼤⼩,所有者,最近修改时间等
• 当前分组所有Inode属性的集合
• inode编号以分区为单位,整体划分,不可跨分区

3.3.6 Data Block

数据区:存放⽂件内容,也就是⼀个⼀个的Block。根据不同的⽂件类型有以下⼏种情况:
• 对于普通⽂件,⽂件的数据存储在数据块中。
• 对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中,除了⽂件名外,ls -l命令看到的其它信息保存在该⽂件的inode中。
• Block 号按照分区划分,不可跨分区

3.4 inode和datablock映射(弱化)

在这里插入图片描述
💡 结论:
• 分区之后的格式化操作,就是对分区进⾏分组,在每个分组中写⼊SB、GDT、BlockBitmap、Inode Bitmap等管理信息,这些管理信息统称: ⽂件系统
• 只要知道⽂件的inode号,就能在指定分区中确定是哪⼀个分组,进⽽在哪⼀个分组确定是哪⼀个inode
• 拿到inode⽂件属性和内容就全部都有了

下⾯,通过touch⼀个新⽂件来看看如何⼯作:
在这里插入图片描述

创建⼀个新⽂件主要有以下4个操作:

  1. 存储属性
    内核先找到⼀个空闲的i节点(这⾥是655524)。内核把⽂件信息记录到其中。
  2. 存储数据
    该⽂件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第⼀块数据复制到300,下⼀块复制到500,以此类推。
  3. 记录分配情况
    ⽂件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  4. 添加⽂件名到⽬录
    新的⽂件名abc。linux如何在当前的⽬录中记录这个⽂件?内核将⼊⼝(655524,pyt)添加到⽬录⽂件。⽂件名和inode之间的对应关系将⽂件名和⽂件的内容及属性连接起来。

3.5 ⽬录与⽂件名

问题:
• 我们访问⽂件,都是⽤的⽂件名,没⽤过inode号啊?
• ⽬录是⽂件吗?如何理解?
答案:
• ⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。
• ⽬录的属性不⽤多说,内容保存的是:⽂件名和Inode号的映射关系

所以,访问⽂件,必须打开当前⽬录,根据⽂件名,获得对应的inode号,然后进⾏⽂件访问。访问⽂件必须要知道当前⼯作⽬录,本质是必须能打开当前⼯作⽬录⽂件,查看⽬录⽂件的内容!

3.6 路径解析

问题:打开当前⼯作⽬录⽂件,查看当前⼯作⽬录⽂件的内容?当前⼯作⽬录不也是⽂件吗?我们访问当前⼯作⽬录不也是只知道当前⼯作⽬录的⽂件名吗?要访问它,不也得知道当前⼯作⽬录的inode吗?
答案1:所以也要打开:当前⼯作⽬录的上级⽬录,额…,上级⽬录不也是⽬录吗??不还是上⾯的问题吗?
答案2:所以类似"递归",需要把路径中所有的⽬录全部解析,出⼝是"/"根⽬录。
最终答案3:⽽实际上,任何⽂件,都有路径,访问⽬标⽂件,⽐如:
/home/whb/code/test/test/test.c
都要从根⽬录开始,依次打开每⼀个⽬录,根据⽬录名,依次访问每个⽬录下指定的⽬录,直到访问到test.c。这个过程叫做Linux路径解析。

💡 注意:
• 所以,我们知道了:访问⽂件必须要有⽬录+⽂件名=路径的原因
• 根⽬录固定⽂件名,inode号,⽆需查找,系统开机之后就必须知道

可是路径谁提供?
• 你访问⽂件,都是指令/⼯具访问,本质是进程访问,进程有CWD!进程提供路径。
• 你open⽂件,提供了路径
可是最开始的路径从哪⾥来?
• 所以Linux为什么要有根⽬录, 根⽬录下为什么要有那么多缺省⽬录?
• 你为什么要有家⽬录,你⾃⼰可以新建⽬录?
• 上⾯所有⾏为:本质就是在磁盘⽂件系统中,新建⽬录⽂件。⽽你新建的任何⽂件,都在你或者系统指定的⽬录下新建,这不就是天然就有路径了嘛!
• 系统+⽤⼾共同构建Linux路径结构

3.7 路径缓存

问题1:Linux磁盘中,存在真正的⽬录吗?
答案:不存在,只有⽂件。只保存⽂件属性+⽂件内容
问题2:访问任何⽂件,都要从/⽬录开始进⾏路径解析?
答案:原则上是,但是这样太慢,所以Linux会缓存历史路径结构
问题3:Linux⽬录的概念,怎么产⽣的?
答案:打开的⽂件是⽬录的话,由OS⾃⼰在内存中进⾏路径维护

Linux中,在内核中维护树状路径结构的内核结构体叫做: struct dentry

3.8 挂载分区

我们已经能够根据inode号在指定分区找⽂件了,也已经能根据⽬录⽂件内容,找指定的inode了,在指定的分区内,我们可以为所欲为了。可是:
问题:inode不是不能跨分区吗?Linux不是可以有多个分区吗?我怎么知道我在哪⼀个分区???

• 分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。
• 所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。(理解到这个层⾯)

4->软硬链接

4.1 硬链接

我们看到,真正找到磁盘上⽂件的并不是⽂件名,⽽是inode。其实在linux中可以让多个⽂件名对应于同⼀个inode。
在这里插入图片描述
• abc和def的链接状态完全相同,他们被称为指向⽂件的硬链接。内核记录了这个链接数,inode655524 的硬链接数为2。
• 我们在删除⽂件时⼲了两件事情:1.在⽬录中将对应的记录删除,2.将硬连接数-1,如果为0,则将对应的磁盘释放。

4.2 软链接

硬链接是通过inode引⽤另外⼀个⽂件,软链接是通过名字引⽤另外⼀个⽂件,但实际上,新的⽂件和被引⽤的⽂件的inode不同(软链接是一个独立的文件,因为它有独立的inode number),应⽤常⻅上可以想象成⼀个快捷⽅式。

4.3 软硬链接对比

• 软连接是独⽴⽂件
• 硬链接只是⽂件名和⽬标⽂件inode的映射关系

4.4 软硬链接的用途

硬链接
• .和… 就是硬链接
• ⽂件备份
软连接
• 类似快捷⽅式


结尾

uu们,本文的内容到这里就全部结束了,道尔在这里再次感谢您的阅读!

道尔柯南

C/C++ & Linux 底层探索者 | 一个正在努力学习的技术博主


👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。
❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。
⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。
💬 【评论】 分享你的经验或疑问,评论区一起交流避坑!

不要忘记给博主“一键四连”哦!

“今日目标达成!”

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2502_90266547/article/details/164400225

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--