Linux文件系统:从磁盘存储到inode结构与文件操作

发布于:2025-07-01 ⋅ 阅读:(24) ⋅ 点赞:(0)

目录

1、磁盘文件

2、磁盘概念

2.1、基本结构

2.2、数据存储

 3. 磁盘信息及分区的意义

3.1 分区的意义

​编辑引导块(Boot Block)

块组(Block Group)

超级块(Super Block)

块组描述符(GDB)

块位图(Block Bitmap)

inode位图(inode Bitmap)

数据区(Data Blocks)

4、inode结构体和inode号

4.1文件的增删查改

文件创建

文件访问

 文件删除

1、磁盘文件

在计算机中,没有被打开的文件都是静静的躺在外存(磁盘)中,当需要对文件进行操作时,会通过 inode 对文件进行访问

通过以下指令查看当前目录中文件的详细信息及 inode 

ll -i

 如同 pid 与进程的唯一对应性一样,inode 与文件也是唯一对应的(未被硬链接的情况下),可以通过 inode 访问文件在磁盘中的详细信息。

磁盘文件是如何进行管理的?

  • 磁盘文件的管理类似于菜鸟驿站,其中的包裹就像待访问的文件,而 inode 就是取件码
  • 得益于这种规范化的存储模式,我们可以做到对文件的快速定位、快速读取和快速写入

 以上的 “管理” 只是抽象概念,具体动作还得对磁盘结构学习后,才能更好的理解。


2、磁盘概念

现在市面上的磁盘主要分为 机械硬盘 和 固态硬盘,前者读取速度慢,但便宜、稳定;后者读取速度快,但价格高昂且数据易损,两者各有其应用场景,本文主要介绍的是 机械硬盘

2.1、基本结构

机械硬盘是我们电脑中的唯一一个机械设备,并且它还是一个外设,根据 冯诺依曼体系结构机械硬盘 在速度上远远慢于 CPU 和 内存。

举例机械硬盘有多慢

  • 假设 CPU 运行速度是纳秒级,那么内存就是微秒级,而机械硬盘只不是是毫秒级

 为何 机械硬盘 如此慢?这与它的结构有很大关系

机械硬盘 的结构主要包括以下几种:

  • 盘片:一片两面,每一面都可以存储数据,有一摞盘片
  • 磁头:一面配备一个磁头,专门用于读取盘面中的数据
  • 主轴:用于控制整块盘的转动
  • 音圈马达:控制磁头的进退
  • 磁头臂:链接磁头与音圈马达
  • 伺服电路板:控制读取数据的流向及各种结构的运行
  • ……

注意: 多个盘片、多个磁头都是共进退的

 机械设备 控制是需要时间的,因此导致 机械硬盘 读写数据速度相对于 CPU 和 内存 来说比较慢

2.2、数据存储

总所周知,数据是以 0 和 1 的方式进行存储的,常见的存储介质有:强信号与弱信号高电平与低电平波峰与波谷南极与北极 等,而盘面上比较适合的是 南极与北极

 当磁头移动到指定位置时

  • 向磁盘写入数据:N->S
  • 删除磁盘中的数据:S->N

磁盘中读写的本质:更改基本元素的南北极、读取南北极

注意: 磁头并非与盘面进行直接接触,而是以 15 纳米的超低距离进行磁场更改

这个距离相当于一架波音747距离地面1米进行超低空飞行,所以如果磁头制作工艺不够精湛,可能会导致磁头在写入/读取数据时,与盘面发生摩擦(高速旋转)发热,从而导致磁场消失,该扇区失效,数据丢失

 机械硬盘 不能在其运行时随意移动,因为角度的偏转也有可能导致发生摩擦,造成数据丢失,更不能用力拍打 机械硬盘

在盘面设计上,一个盘面被切割若干个扇区,单个扇区大小为 512 字节(或者 4 kb),这些扇区用来存储数据,同一半径中的所有扇区组成扇面;而半径相同的扇区组成磁道(柱面)

我们可以先根据磁头(head)确定盘面,再根据半径定位磁道(柱面 cylinder),最后根据块号确定扇区(sector)

  • 这种寻址方法称为 CHS 定位法,是机械设备查找具体扇区时的方法

文件(数据+属性)在存储时,占用一个或多个扇区进行数据存储。

虽然 CHS 定位法很妙,但它太依赖于具体硬件信息了,假设其中的硬件参数有所不同,那么 OS 就得使用另一套 CHS 定位法,于是为了做到 解耦OS 使用的并非 CHS 定位法进行文件定位,而是采用 LBA 逻辑地址块进行寻址。

将盘面分割为多个线性分区,通过下标 N 计算出 CHS 地址,然后进行文件访问

将磁道拉长,会得到一串线性空间(数组),其中的每个单位(扇区)为 512 字节(或者 4 kb

 现在 OS 想访问具体的扇区时,只需通过 起始扇区的地址 + 偏移量 就可以获取 LBA 地址,然后通过特定手段转为 CHS 地址,交给外设进行访问即可 LBA和CHS转换

 为什么现代I/O过程一次性读取4kb大小数据,与内存对齐有什么关系?

答:数据块大小由文件系统决定,一般数据块大小为4kb,而内存页大小也为4kb,当操作系统向磁盘读取数据时,能够 1.提高缓存效率:读取 4KB 数据块意味着将数据更好地适配到内存页(memory page)中,这使得 CPU 可以通过高效的内存访问模式(如内存缓存)来提高 I/O 性能。2.减少磁盘访问次数:如果每次读取的数据小于 4KB,磁盘仍然需要以 4KB 为单位进行读取,这样会导致较高的 I/O 操作次数和不必要的存储带宽浪费。

内存对齐是指将数据放置在内存中使得其起始地址符合硬件架构的要求,以优化 CPU 对数据的访问效率。许多 CPU 和内存系统要求数据的读取与写入必须在特定的内存地址边界上进行。例如,32 位的系统通常要求 4 字节的数据起始地址是 4 的倍数。

  • 件系统通常会将文件按 4KB 的块进行组织,并对磁盘进行对齐。这样做的目的是为了减少磁盘碎片和提高读取效率。

  • 当操作系统从磁盘读取数据时,它会读取一个完整的 4KB 数据块,这样就避免了部分块读取时可能发生的数据碎片问题。这种方式使得操作系统能够有效地管理文件数据,避免读取数据时的浪费和额外的磁盘寻道。

 3. 磁盘信息及分区的意义

在大多数计算机系统中,磁盘被分为多个分区,比如常见的 C 盘、D 盘等。这种分区方式对于操作系统的管理、存储设备的使用和数据的安全性等方面都具有重要意义。

3.1 分区的意义

磁盘空间的大小和复杂性使得不对其进行合理划分时,操作系统在管理和访问数据时的开销会非常大。为了提高效率和降低管理成本,操作系统会将磁盘空间划分成多个分区。这种做法借鉴了分治思想,类似于现实生活中的学校将不同专业的学生和老师分配到不同的学院,通过分配和划分,便于更高效的管理和资源利用。

分区的主要作用:

  1. 简化管理

    • 通过将磁盘划分为多个小的、独立的部分,操作系统可以更容易地管理不同的磁盘区域。每个分区可以看作一个独立的存储单元,便于管理和分配存储空间。

  2. 提高安全性和稳定性

    • 每个分区可以用于不同的用途(例如,一个分区用于操作系统,另一个分区用于存储数据)。如果一个分区损坏,其他分区的数据不会受到影响。

  3. 性能优化

    • 通过分区,操作系统可以对不同类型的文件采用不同的存储方式。例如,操作系统和应用程序可以存放在一个分区上,而用户数据存储在另一个分区上。这样可以提高性能,因为磁盘的访问模式可能不同。

  4. 便于备份和恢复

    • 分区使得数据的备份和恢复更加灵活。如果系统分区和数据分区是分开的,恢复操作系统的过程不会影响到数据分区,从而更容易进行数据恢复。

  5. 灵活性和扩展性

    • 分区还允许操作系统灵活地管理和划分磁盘空间,可以方便地调整分区大小或对不同分区进行不同的操作,如格式化、挂载等。

磁盘分区示例

在文件系统中,操作系统通常会使用一个结构体(如 struct disk)来管理磁盘及其分区。以下是一个可能的分区结构:

struct disk {
    struct part[2];  // 每个磁盘可以包含多个分区(part)
    // 其他与磁盘相关的管理信息
};

通过这种方式,磁盘的管理信息被组织成一个结构体数组,每个磁盘对应一个 struct disk,每个磁盘下可能包含多个分区(part)。操作系统通过这些结构体来管理分区的布局、大小、使用情况等。

如何查看磁盘分区信息:

在 Linux 系统中,你可以通过以下命令查看磁盘分区的详细信息:

ll /dev/vda* -i

 统在分区后,需要对区块进行格式化

不同的文件系统在格式化时写入的数据是不同的,这里讨论的是 EXT 文件系统

  • 磁盘分区后,分组、填写系统属性是 OS 做的事

  • 为了使分区能被正常使用,需要对分区进行格式化

  • 分区格式化:OS 向分区写入文件系统的管理属性信息

  • 在具体分区内,还可以细分为 块组

由 块组 构成的线性空间亦可称为 组线 (代表一个 分区

struct part
{
    struct part group[100];	//分为100个块组
    int lba_start;	//起始与结束位置
    int lba_end;
    //……
}

 将现有资源再分配后,可以 最大化利用资源,避免造成浪费及拖慢效率

块组(Block Group)是本文的重点内容。

块组 是由 分区 细分出的产物,它与分区的关系如图所示

引导块(Boot Block)

引导块(Boot Block)是文件系统中的一个特殊块,用于存储磁盘分区信息和启动信息。通常位于分区的第一个块,其大小通常为1KB,这是由PC标准规定的。

功能与作用:

存储引导加载程序:Boot Block中存放的是boot loader(引导加载程序),这段程序在开机启动时被加载,用于引导操作系统的启动过程。
系统启动的关键:Boot Block对于操作系统的启动至关重要。如果Boot Block中的数据有误或丢失,操作系统可能无法启动。

与Linux文件系统的关系:

文件系统结构:Linux文件系统(如ext2、ext3、ext4等)会将磁盘划分为多个Block Group(块组),但Boot Block是独立于这些块组之外的特殊区域。
数据组织:虽然Boot Block不直接参与文件数据的存储,但它确保了系统能够正确地启动并加载操作系统,从而进一步访问和管理存储在磁盘上的文件和数据。
Bootblock Linux的特殊说明

Bootblock Linux:这是一个适用于嵌入式设备和嵌入式系统的轻量级Linux操作系统。其引导块(bootblock)是该系统启动的核心组件之一,采用了精简的设计和优化的代码,以减少启动时间和资源消耗。
应用场景:Bootblock Linux非常适合于资源有限的设备,如路由器、智能家居设备和物联网设备等。
总结:

Boot Block是Linux文件系统中一个关键且特殊的区域,它存储了引导加载程序,对于操作系统的启动至关重要。了解Boot Block的功能和作用有助于更好地理解Linux文件系统的整体结构和启动过程。同时,对于特定于嵌入式系统的Bootblock Linux来说,其引导块的设计和优化更是体现了其在资源受限环境下的高效性和稳定性。

块组(Block Group)

块组(Block Group):是Linux文件系统中的一个逻辑单元,用于组织和管理磁盘上的数据块(Blocks)和inode节点。ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子。

超级块(Super Block)

超级块(Super Block):存放文件系统本身的结构信息。以下是一些典型的Super Block内容:

  • 文件节点(inode)计数:记录文件系统中inode的总数、已用数和空闲数。inode是存储文件元数据(如权限、所有者、大小、时间戳等)的数据结构。
  • 块(block)计数:记录文件系统中块的总数、已用数、空闲数和保留数。块是文件系统中存储文件数据的基本单位。
  • 块大小和块组:指定块的大小以及文件系统被划分为多少个块组(block group)。每个块组包含一定数量的块、inode和Super Block的备份(如果有的话)。
  • 时间戳:记录文件系统最后一次被挂载、写入和检查的时间。
  • 状态标志和错误处理:记录文件系统的当前状态以及遇到错误时的处理策略。

 Super Block是文件系统中不可或缺的组成部分,由于Super Block对文件系统的重要性,许多文件系统都提供了Super Block的备份机制。  

块组描述符(GDB)

块组描述符(Group Descriptor Table,GDB):描述块组属性信息,如块组的状态、位置等。它通常包含了关于该块组的元数据,如:

  • 块组中数据块的数量和状态(已用、空闲等)。
  • inode的数量和状态。
  • 块位图和inode位图的起始位置。
  • 可能的块组和inode表的起始位置。
  • 其他与块组管理相关的参数。

块组描述符的作用在于提供对块组内数据和元数据的快速访问和管理。通过读取块组描述符,文件系统可以快速确定块组中哪些数据块和inode是可用的,哪些已经被占用,从而有效地进行文件数据的读写和元数据的管理。 

块位图(Block Bitmap)
  • 块位图(Block Bitmap):是一种位图数据结构,记录inode节点的使用情况,即哪个数据块已经被占用,哪个数据块没有被占用。

功能:

  • 空间管理:块位图使得文件系统能够快速地查找空闲的存储块,以便为新文件或目录分配空间。同时,它也能快速确定哪些存储块已被使用,从而避免数据覆盖等错误。
  • 性能优化:通过块位图,文件系统可以减少对存储介质的访问次数,提高数据读写效率。例如,在分配新空间时,只需扫描块位图即可找到空闲块,而无需遍历整个存储介质。
inode位图(inode Bitmap)
  • inode位图(inode Bitmap):inode位图是一个位数组(bit array,记录inode节点的使用情况,即每个bit表示一个inode是否空闲可用。

作用

  • inode管理:inode位图使得文件系统能够快速地查找空闲的inode,以便为新文件或目录分配inode。同时,它也能快速确定哪些inode已被使用,从而避免inode的重复分配。
  • 空间优化:通过inode位图,文件系统可以更有效地管理inode空间,减少空间的浪费。
  • 性能提升:在文件系统的操作中,inode的分配和释放是常见的操作。inode位图通过提供快速的查找和更新能力,提升了这些操作的性能。

2.7、inode节点表(inode table)
inode节点表(inode table):存放文件属性,如 文件大小,所有者,最近修改时间等。

Linux中文件的属性是一个固定大小的集合。

struct inode
{
    int size; // 文件的字节数:表示文件的大小,即文件包含的数据量。
    mode_t mode; // 权限掩码
    int creater; // 文件创建者
    int time; // 文件的时间戳,ACM时间
    ...
    int inode_number;// inode 编号
    int datablocks[N];
}

  1. 该集合大小为128字节。
  2. inode内部不包含文件名!
  3. 内核层面,每一个文件都要有inode number!
  4. 我们通过inode编号标识一个为文件!
  5. 一个正常文件,一个inode集合!

 stat

将显示指定文件或目录的详细信息。

语法

stat [选项] 文件或目录

常用选项:

  1. -c, --format=<格式>:指定自定义格式输出文件信息。在格式字符串中,可以使用特殊转义序列来指定要显示的信息,如%n(文件名)、%s(文件大小)、%y(最后访问时间)等。

  2. -f, --file-system:显示文件所在文件系统的信息,包括文件系统的名称、挂载点、类型、大小、可用空间等。

  3. -L, --dereference:显示符号链接的原始文件信息,而不是符号链接本身的信息。

  4. -t, --terse:以紧凑格式输出文件信息,便于快速查看。

  5. -x, --xml:以XML格式输出文件信息,便于程序处理和解析。

  ls -i 命令也可以查看inode编号

数据区(Data Blocks)

数据区(Data Blocks):存放文件内容。文件内容被分割成多个块,存储在数据区中。

  • Data Block是文件系统中用于存储数据的最小单位,通常具有固定的大小。
  • Data Blocks的大小和数量在文件系统格式化时设置,并且一旦设置,通常不能更改(除非重新格式化文件系统)。

 竟然数据区的大小通常是固定的,那么它是如何存储不同大小的数据的呢?

存储机制

  1. 直接存储
    • 当文件大小小于或等于一个Data Block的大小时,文件内容将完整地存储在一个Data Block中。
    • 剩余的Data Block空间(如果有的话)将保持空闲,直到被其他文件内容占用。
  1. 间接存储
    • 当文件大小超过一个Data Block的容量时,文件内容会被分割成多个部分,并存储在多个Data Blocks中。
    • 每个Data Block存储文件内容的一部分,并通过某种方式(如inode中的指针)相互链接,以表示文件的完整内容。

4、inode结构体和inode号

inode 结构体

**inode(索引节点)**是文件系统中用来存储文件元数据的数据结构,它包含了文件的所有关键信息(除了文件名和实际数据)。每个文件或目录都有一个唯一的 inode 来表示其元数据。inode 不包含文件名,它通过与文件名在目录项中的映射来查找。

inode 结构体的组成:

通常情况下,inode 结构体包含以下内容:

struct inode {
    int size;           // 文件的字节数,即文件内容的大小
    mode_t mode;        // 文件的权限掩码,包括文件类型、权限等信息
    int creater;        // 文件的创建者(UID)
    int time;           // 文件的时间戳(如创建时间、修改时间等)
    int inode_number;   // inode 编号,每个文件或目录在文件系统中的唯一标识符
    int datablocks[N];  // 数据块指针,指向文件内容所在的磁盘数据块,通常是直接指针、间接指针等
    ...
};

主要字段说明:

  1. size:文件的大小,表示文件数据的字节数。

  2. mode:文件的权限掩码,它定义了文件类型和访问权限(如文件的读/写/执行权限,文件是否为目录等)。

  3. creater:文件的创建者,通常是文件的拥有者(存储其用户 ID)。

  4. time:文件的时间戳,记录文件的创建、修改和访问时间。

  5. inode_number:文件在文件系统中的唯一标识符。每个文件或目录都有一个唯一的 inode 编号。

  6. datablocks[N]:指向文件内容所在数据块的指针。对于较小的文件,文件内容可以直接存储在 inode 中的指针位置。对于较大的文件,可能会使用直接指针、间接指针等方式来指向多个数据块。

作用:

  • inode 是文件系统中对文件或目录的所有元数据(如大小、权限、时间戳等)的集中管理结构。

  • 通过 inode,操作系统可以访问文件的所有属性和数据,而无需依赖于文件名。

  • inode 不存储文件名,文件名和 inode 通过目录项(dentry)来关联。

文件=属性+数据,每一个文件的属性都存在inode结构体中,而inode中的inode号码是文件区分的唯一标识,文件的数据存在数据块中。目录也是一个文件 ,它也有inode结构体,但是它的数据块中存储的是目录下面的映射关系  文件名对应inode。一个文件名对应一个inode码。 

4.1文件的增删查改
文件创建

创建一个文件的步骤如下:

1.申请一个空闲的 inode,将文件信息记录至 inode 属性中
2.寻找空闲的数据块(Data block),将数据块信息填入 inode 中的磁盘分布区
3.添加文件名至当前目录文件的 Data block 中,同时将文件名和 inode 之中的属性链接起来

注意: 每使用一个 inode 和一个 Data block,需要把它们对应位图中的信息改为已占用

文件访问

文件创建后,如何根据 inode 访问文件呢?

找到文件的 inode 编号,在目录分组中查找
通过 inode 和 Data block 的映射关系,找到文件的数据块,并加载至内存中
这也就解释了为什么在 file 对象中会存在 inode 信息,因为它与 fd 一样重要

 文件删除

文件创建后,如何删除?删除并不是真删除,而是将 inode Bitmap 和 Block Bitmap 中位图信息进行修改即可(只要访问不到,就是删除)

  • 根据文件名找到 inode 编号

  • 再根据 inode 属性中的映射关系,设置 Block Bitmap 对应的比特位,设置为 0 (删内容)

  • 最后根据 inode 编号设置 inode Bitmap 中

    将位图信息置为 0 后,创建新文件时,系统可以直接使用

    至于文件的查找与修改,通过 inode 修改其内部属性即可

 为什么同一个目录下不能有同名文件?

同一个目录下不能有同名文件,因为文件名与 inode 通过目录项关联,每个文件名必须唯一。操作系统通过文件名查找 inode,如果存在同名文件,就无法确定访问哪个 inode,从而导致混乱。因此,文件系统要求每个目录下的文件名是唯一的。 

 目录没有权限 w ,我们无法删除文件,没有 r,我们无法查看文件是为什么?

因为目录不给我们,文件名与inode的映射关系

r 权限与 inode 映射

  • r 权限允许用户读取目录的内容,即列出目录中的文件。没有 r 权限,无法查看目录项,因此无法知道目录中有哪些文件或目录,无法通过目录的 inode 映射关系来获取文件名和 inode 信息。

. w 权限与 inode 映射

  • w 权限允许用户修改目录内容,包括 创建、删除和重命名文件。删除文件时,操作系统通过目录项来查找文件的 inode,然后从目录中移除该文件的映射。如果没有 w 权限,不能修改目录的内容(即不能删除或添加目录项),因此无法删除文件

 目录是文件,也有 inode 编号,那么是如何管理的呢?

使用文件时, 文件的inode存储在目录的数据块中,而目录也是文件,也需要找inode,所以会形成一层一层向上找,呈现出递归。这样一来。每一次找文件都太过麻烦,所以引出了dentry 缓存

*dentry 缓存(目录项缓存)**是操作系统中用来加速文件系统路径查找过程的一个重要机制。它用于缓存文件路径中的目录项(dentry)与 inode 之间的映射关系,从而提高文件系统的性能,减少磁盘访问次数。

1. dentry 的定义:
在类 Unix 系统中,dentry(目录项)是指目录中文件名与 inode 号之间的映射关系。具体来说,它表示了一个路径组件(例如一个目录或文件)与对应 inode 之间的关联。

每个目录项(dentry)都包含:

文件(或子目录)的名字。

对应的 inode 号,指向该文件或目录的 inode 结构体。

该目录项在路径中与其他目录项的层次关系。

2. dentry 缓存的作用:
加速路径查找:文件系统路径查找涉及从根目录开始,依次进入每个子目录,并在每个目录中查找对应的文件或子目录。这一过程可能非常慢,尤其是在大文件系统中。dentry 缓存缓存了路径中每个目录项的映射关系,允许操作系统快速查找文件路径。

减少磁盘访问:每当一个目录被查找时,系统会检查 dentry 缓存中是否已经存在该目录的目录项。如果存在,操作系统可以直接从缓存中获取映射关系,无需再次访问磁盘。

提高效率:通过避免重复查找文件和目录,dentry 缓存显著提高了文件系统的性能,尤其是在重复访问相同路径时。

3. dentry 缓存如何工作:
当文件系统访问一个文件路径时,系统会首先检查 dentry 缓存,看是否已经缓存了路径中涉及的目录项(dentry)。如果缓存命中,操作系统直接从缓存中读取该信息;如果缓存未命中,则需要读取磁盘来查找该目录项,并将其添加到 dentry 缓存中。

在 dentry 缓存中,每个目录项都有一个对应的 inode,当操作系统查找目录项时,它能迅速找到对应的 inode 号,从而获取文件的元数据(如权限、大小、位置等)。

4. dentry 缓存的生命周期:
缓存刷新:dentry 缓存是动态管理的,当文件系统中的目录结构发生变化时(如文件被删除或移动),dentry 缓存会相应更新。

缓存淘汰:操作系统会定期清理不再使用的 dentry 项,以腾出空间给新的目录项。缓存中的 dentry 项会有一个引用计数,当该目录项不再被引用时,它会被从缓存中移除。

5. dentry 缓存与 inode 缓存的区别:
inode 缓存:存储的是文件的元数据(例如权限、大小、时间戳等),指向存储文件内容的磁盘块。

dentry 缓存:存储的是文件路径中各目录项(即目录中的文件名与 inode 的映射),用于加速路径查找。

6. dentry 缓存的优势:
提高文件路径查找的速度。

避免多次访问磁盘,提高系统响应速度。

减少磁盘I/O操作,优化系统的整体性能。

7. dentry 缓存的清理和管理:
dentry 缓存并不是永久存在的,当文件系统的内存资源紧张时,操作系统会清除不再使用的缓存项。

系统通过引用计数和使用频率来管理 dentry 缓存项,确保活跃的目录项得到优先保留。

总结:
dentry 缓存是操作系统用于缓存目录项和 inode 映射关系的机制,它通过缓存文件系统路径中的目录项(dentry)来加速路径查找操作,减少磁盘访问次数,从而显著提高文件系统的性能。