Linux 之 【文件】(ext2文件系统、目录、软硬链接)

目录

1.文件系统(ext2)

1.1核心概念

1.2细节

(1)跨盘面存储

(2)格式化

(3)数据块

(4)inode

(5)位图

(6)新建文件的过程

(7)删除文件的过程

(8)查找文件的过程

(9)修改文件的过程

(10)文件名与inode编号

(11)目录的理解

(12)软链接

(13)硬链接


1.文件系统(ext2)

1.1核心概念

磁盘的物理存储空间巨大且呈现为连续的扇区序列。为了高效地组织和使用这些空间,操作系统采用分层管理策略:

磁盘分区 :首先,通过分区表将整个物理磁盘的线性地址空间,划分为若干个逻辑上独立的连续区域,称为分区每个分区在操作系统看来就像一个独立的"虚拟磁盘"

格式化与ext2文件系统 :然后,在分区上创建ext2文件系统 。ext2采用经典的"块组(Block Group)"设计来管理分区空间:

  1. 块组划分 :它将整个分区划分为一系列大小相等的块组每个块组内部自包含关键的元数据:超级块、组描述符表、块位图与inode位图、inode表、数据块

  2. 核心工作流程 :ext2通过inode机制建立层次化命名空间:

    • 每个文件/目录对应一个唯一的inode,存储其所有元数据和数据块指针

    • 目录本质上是一种特殊文件,其数据块存储着"文件名→inode号"的映射表

    • 通过逐级查找目录项,最终定位到文件的inode,再通过inode找到实际的数据块

  3. 设计特点

    • 无日志功能 :崩溃后需要全盘检查(e2fsck

    • 固定inode预先分配:inode数量在格式化时确定

    • 直接/间接块指针:采用多级索引机制支持大文件

组件 作用 类比
超级块 记录整个文件系统 的全局信息:总块数、inode数、块大小、挂载状态等。它是文件系统的"总控制台" 整个城市的总体规划图。
组描述符表 描述每个块组的详细信息:本组的块位图、inode位图、inode表位于哪个块。 每个区的详细地图索引。
块位图 一个比特位代表本组内的一个数据块1=已占用,0=空闲。用于快速分配/回收数据块。 区内每个停车位的占用情况表。
inode位图 一个比特位代表本组内的一个inode1=已占用,0=空闲。用于快速分配/回收inode。 区内每个档案柜编号的占用情况表。
inode表 核心数据结构 。存储本组所有 inode 结构体。每个inode约128字节,记录一个文件/目录 的元数据(权限、大小、时间戳)和指向数据块的指针 档案柜。每个抽屉(inode)存放一份文件的档案卡(元数据),并标明了文件内容存放的位置(指针)。
数据块 实际存储文件内容目录条目的区块。 实际存放文件纸张的仓库,或目录的清单列表。

一个分区可以格式化为一个 ext2 文件系统

该文件系统被划分为多个块组,全局管理组件 (超级块、组描述符表等)有选择地在多个块组中备份,而本地管理组件(位图、inode表、数据块等)则每个块组独立一套

复制代码
一个ext2文件系统(占据整个分区)
├── 块组0
│   ├── 超级块(主副本)          ← 文件系统全局组件
│   ├── 组描述符表(所有组的描述)← 文件系统全局组件
│   ├── 块位图(仅管本组)
│   ├── inode位图(仅管本组)
│   ├── inode表(仅存本组inode)
│   └── 数据块(仅存本组数据)
├── 块组1
│   ├── 超级块(冗余副本)        ← 全局组件的备份
│   ├── 组描述符表(冗余副本)    ← 全局组件的备份
│   ├── 块位图(仅管本组)
│   ├── ...(其他本组组件)
│   └── 数据块
└── 块组N(结构同块组1)

文件=文件内容+文件属性 存储文件=存文件的内容+存文件的属性,在ext2文件系统中

inode 用于存放文件属性,数据块 用于存放文件内容,所以

Linux的文件在磁盘中存储,实质上是将属性和内容分开存储的

1.2细节

(1)跨盘面存储

  • 磁盘分区操作的是逻辑块地址空间,与物理盘面分布无关。文件系统在逻辑地址上工作,物理地址转换由磁盘控制器自动完成,因此跨盘面存储不会造成任何问题
  • 在ext2文件系统中boot block与开机有关,这里不做介绍

(2)格式化

格式化是指在使用一个分区前,预先将特定文件系统的结构信息(如超级块、组描述符、位图等)写入该分区的过程。格式化操作使得分区能够被操作系统识别和管理,以便后续正常使用该分区

(3)数据块

  • Data blocks 是文件系统中用于存储实际文件内容 的区域。它以 (或称"簇")为基本单位进行分配和管理,块大小通常为 4KB(是物理扇区 512B 或 4KB 的整数倍)。每个块默认只存储同一个文件 的数据,但一个文件的完整内容通常会占用多个连续或不连续的块
  • 数据块编号从0开始

(4)inode

在ext2文件系统中,inode 是一个固定大小为128字节的数据结构(结构体),用于存储除文件名外的单个文件或目录的所有元数据属性和数据块指针

  • 每个文件或目录严格对应一个唯一的inode;
  • 分区在格式化时即固定其inode总数和数据块总数
  • 每个分区维护自己独立的inode编号序列,编号在分区内唯一;
  • 不同分区的inode表互不关联,即使编号相同也代表完全不同的文件
  • 在 ext2 文件系统中,inode 编号 **0 被保留为无效值,**有效 inode 编号从 1 开始,但前几个编号(1-10)为系统保留,用户文件通常从编号 11 开始分配
  • inode就像是文件的一个整体的描述,有了这个描述,上层就可以重新组织虚拟逻辑结构,通过inode映射其物理结构

因为一个分区所能使用的inode总数和数据块总数是确定的,所以存在以下情况:

inode有剩余,数据块用完了;数据块有剩余,inode用完了
在Linux文件系统中,文件的属性(元数据)存储在inode中,不包括文件名。文件名保存在目录项里,与inode号形成映射。在分区内,inode编号唯一标识一个文件;在整个系统中,需要设备号 (不做讲解)+inode号(ls -i 可以查看)的组合才能全局唯一标识
以 ext2 为例,inode 中包含一个 15 个元素的块指针数组 i_block[15] ,采用 多级混合索引机制 来定位文件的数据块

  • 直接指针:i_block[0] 到 i_block[11] 共 12 个元素,每个直接存储一个数据块的编号,这些数据块直接存放文件内容
  • 一级间接指针:i_block[12] 存储一个间接块的编号,该间接块中存储的是数据块的编号(而非文件内容本身)
  • 二级间接指针:i_block[13] 存储一个二级间接块的编号,该块中存储的是间接块的编号,间接块再存储数据块的编号
  • 三级间接指针:i_block[14] 存储一个三级间接块的编号,该块中存储的是二级间接块的编号,依此类推

(5)位图

块位图(Block Bitmap)中,每个比特位对应分区中的一个数据块

映射关系 :比特位的位置索引(从 0 开始)直接映射到对应的块编号

比特值含义1 表示该数据块已被占用0 表示空闲可用

删除文件操作 :无需擦除数据块内容,只需将该文件占用的所有数据块对应的比特位置为 0,这些块即被标记为可重新分配
inode 位图(inode Bitmap)中,每个比特位对应分区中的一个inode

映射关系:比特位的位置索引(从 0 开始)直接映射到对应的 inode 编号

比特值含义 :1 表示该 inode已被使用 ;0 表示空闲可用

删除文件操作 :无需修改 inode 表中的内容,只需将该文件对应的 inode 编号的比特位置为 0,该 inode 即被标记为可重新分配
inode编号减去本块组inode起始编号,就是该inode在位图中的对应位置

(6)新建文件的过程

新建文件的本质是在目录树中建立一个名称(文件名)到索引(inode编号)的映射,并通过位图机制分配和初始化所需的inode与数据块资源

  1. 路径解析与目录查找

    操作系统解析文件路径,定位到父目录的inode。通过读取目录项数据块,确认目标文件名不存在。同时检查用户是否在父目录中拥有写(w)和执行(x)权限,以创建新文件

  2. 分配与初始化inode

    确定块组 :根据分配策略(如平衡负载或目录的局部性)确定在哪个块组创建文件。
    分配inode :在该块组的 inode位图 中找到一个空闲的inode编号,立即将对应比特位置为"已用"。
    初始化inode:在 inode表 中找到该inode,写入基础元数据:文件类型(常规文件)、权限、所有者ID、链接数(初始为1)、文件大小(初始为0)、时间戳等。

  3. 分配数据块并写入内容(如文件有内容)

    如果文件创建时包含初始数据或大小不为零:
    分配数据块 :系统在对应的块组中查找数据块位图,寻找所需数量的空闲数据块。
    更新数据块位图 :立即将数据块位图中对应的比特位标记为"已用"。
    更新inode :将已分配的数据块编号填入该文件的 inode 的直接/间接块指针数组中。
    写入数据:系统将文件的内容数据写入这些新分配的数据块。

  4. 更新目录项

    在父目录的数据块中,新增一个目录项。该目录项至少包含两部分:文件名 和 新文件的 inode编号。

  5. 同步元数据

    更新父目录inode 的修改时间、大小等。

    递减相关块组描述符和全局超级块中的空闲inode和空闲数据块计数。

(7)删除文件的过程

删除文件的本质是解除文件名与 inode 编号在目录中的映射关系(即删除目录项)。当此操作导致该 inode 的链接数降为 0 时,系统通过位图将其关联的 inode 和数据块标记为"空闲",从而允许这些存储空间被后续的新文件重新分配和覆盖。

  1. 路径解析与权限检查

    操作系统根据文件路径,定位 到父目录。

    在父目录的数据块中,查找目标文件名及其对应的 inode编号
    检查 用户对该文件和其父目录是否拥有必要的删除权限

递减链接数并删除目录项

系统根据inode编号找到对应的 inode结构。

将inode中的 硬链接计数减1。

在父目录的数据块中,立即删除或标记该目录项为无效(例如,将条目的inode编号字段置为0),从而解除文件名到该inode的映射。
3.

判断是否回收资源

如果链接数减后为0:表示此inode已无任何目录项引用,可以安全回收其占用的所有资源。执行以下第4步。

如果链接数仍大于0:表示该inode仍有其他硬链接指向它。流程到此结束,仅完成目录项删除,不进行后续资源回收。
4.

回收inode与数据块资源

回收数据块 :系统读取inode中的块指针数组,找到文件占用的所有数据块。在数据块对应的块组数据块位图中,将这些块对应的比特位清零,标记为"空闲"
回收inode :在该inode所属块组的 inode位图 中,将此inode编号对应的比特位清零,标记为"空闲"。

(可选)清空inode表中的对应项,以增强安全性。

删除文件的核心动作是 1) 解除目录中的名称映射 ,这可能触发 2) 回收底层存储资源 。所有"回收"操作都体现为在位图中将对应比特位标记为空闲,从而实现逻辑上的释放,为后续重用做好准备
操作系统删除文件时,不会立即擦除磁盘上的实际数据,这主要是出于性能考虑------物理擦除大量数据块将带来巨大的时间开销
文件系统仅通过修改元数据(位图)来标记空间可用,这使得数据恢复在理论上可行

  • 从技术原理上讲,要"恢复"一个刚被删除的文件,需要:
  • 知道文件的设备号和 inode 编号(定位)
  • 将 inode 位图中对应比特位重新置 1(恢复 inode 分配状态)
  • 将块位图中该文件所有数据块对应的比特位重新置 1(恢复数据块分配状态)
  • 恢复目录项(在父目录中添加文件名到该 inode 的映射)(重建目录项)
  • 然而,这仅仅是理论可能。实际操作面临多重障碍:
  • 目录项已被清除,难以确定原文件名和路径
  • inode 内容可能已被部分覆盖(如时间戳更新)
  • 数据块可能已被新文件重用
  • 需要绕过文件系统直接修改磁盘元数据,风险极高且需特殊工具
  • 现代文件系统(如 ext4)的延迟分配等特性会使情况更复杂

(8)查找文件的过程

查找文件的本质是根据路径逐级解析目录项,通过文件名获取其 inode 编号,进而定位 inode 以读取文件属性与数据块指针,最终访问文件内容

  1. 路径解析与目录遍历

    从根目录 / 或当前目录开始,将文件路径按 / 分割成一系列分量。

    从起始目录的inode出发,读取其数据块(这些块中存储的是目录项列表)。

    在当前目录的数据块中,线性查找 下一个路径分量对应的目录项,从中获取其 inode编号。

    重复此过程(根据inode编号找到下一级目录的inode,再读其数据块进行查找),直至找到目标文件的直接父目录

定位目标文件inode

在父目录的数据块中,查找目标文件名对应的目录项

如果找到,则从该目录项中取出目标文件的 inode编号。

如果未找到,则返回"文件不存在"错误。
3.

读取inode(获取文件元数据与内容指针)

系统根据获取到的inode编号,计算出其所属的块组 (块组号 = (inode编号 - 1) / 每块组inode数)。

在该块组的inode表 中定位 到该inode的具体条目。
读取inode结构体,获取文件的所有属性(类型、权限、大小、时间戳等)以及指向文件内容数据块的指针数组。
4.

访问文件内容

当需要读取文件数据时,系统根据inode中的指针(直接指针、间接指针),计算出目标数据所在的逻辑块号。

通过文件系统的映射机制,将逻辑块号转换为物理磁盘上的数据块地址。

最后,从该地址读取相应的数据块内容

(9)修改文件的过程

修改文件是在查找定位后,通过分配/释放数据块并更新数据与元数据(如inode中的大小、时间、指针)来完成内容变更的过程

(10)文件名与inode编号

文件名不包含在 inode 结构体中,使得

  • 支持硬链接,可以有多个文件名指向同一个文件
  • 目录操作更简洁:重命名文件时,只需在父目录的数据块中修改或创建一个新的目录项,然后删除旧的目录项即可,速度更快,且不触及文件本身的inode;移动文件时,本质是在新目录创建目录项,在旧目录删除目录项。文件本身的inode和数据块完全不动
  • 结构清晰:inode 回答"这个文件是什么?(类型、权限、大小)"、"它的内容在哪里?(块指针)"目录项 回答"在这个文件夹里,这个叫什么名字?(文件名到inode的映射)"

在正常路径访问中,永远是先有文件名,后得到inode编号

(11)目录的理解

目录是一个文件,有自己独立的inode,也有自己的属性

目录的内容是目录项的集合,目录项是目录内容的基本单位

文件的文件名与对应文件的 inode编号 的映射关系是目录内容的重要目录项

所以

  • 在同一个目录下,不能存在两个完全相同的文件名。这类似于哈希表或键值对中的键必须唯一。其根本原因是:目录的数据结构(目录项列表)要求文件名作为唯一键来查找对应的inode编号。如果允许重名,通过文件名进行路径解析时将产生歧义,无法确定指向哪个inode
  • 写权限实质上是允许对"文件名到inode编号"的映射表进行增删改;目录没有 w 权限,无法建立映射关系,进而无法创建文件
  • 目录没有 r 权限,就无法查看映射关系,无法通过inode编号索引文件进而无法查看文件
  • x 控制能否使用此表进行路径查询和文件访问

所以查找文件是逐级解析路径的过程:

  • 系统从起点(根目录或当前目录)开始,依次处理路径中的每一个分量(由 / 分隔的部分)。对于每个分量:
  • 在当前目录的数据块中,线性搜索对应的目录项以获取其 inode 编号。
  • 然后进入下一级目录(使用刚获得的 inode 编号),重复此过程,直至处理完所有分量,最终得到目标文件的 inode 编号。

Linux 通过目录项缓存(了解)来避免对常用路径的重复磁盘查找,从而显著提升效率

(12)软链接

软连接(符号链接) :创建一个独立的特殊文件 ,其内容是指向目标文件的路径字符串(包含文件名),可以跨文件系统

跨文件系统:操作涉及多个独立的存储管理单元,每个单元有自己的编号体系和管理规则,彼此不共享内部标识(如inode)
软链接能跨文件系统,因为:

软链接的本质是存储目标文件的路径字符串。

  • 它不引用 inode,只记录文本路径
  • 路径字符串在任何文件系统上都能被理解
  • 就像写张纸条"去北京找张三",在哪都能看懂这指令

创建软链接

复制代码
ln -s 目标 链接名//创建软链接
  • 软链接是一个独立的文件,拥有独立的inode与数据块

  • 其数据块中存储的是目标文件的路径字符串。这个特性与 Windows 快捷方式 类似

  • 不能轻易删掉软连接所指向的目标文件,否则软链接会变成"悬空链接",访问它会提示"No such file or directory"

  • 软链接文件是一个独立的文件,有自己的inode节点,这个文件数据中保存的是源文件路径,通过保存的路径访问源文件,因此源文件被删除则无法再访问,通过路径将找不到源文件,这时候软链接就会失效

  • unlink 链接名
    rm 链接名

  • unlink 链接名 和 rm 链接名 都可以安全地删除一个软链接,且两者都只删除链接本身,不会影响其指向的目标文件

应用场景

  • 简化命令:为长路径的可执行文件创建软链接至 $PATH 目录中(如 /usr/bin)。

(13)硬链接

硬链接:在目录中创建一个新的目录项,直接指向同一个inode(同一物理文件),不能跨文件系统。
跨文件系统:操作涉及多个独立的存储管理单元,每个单元有自己的编号体系和管理规则,彼此不共享内部标识(如inode)
硬链接不能跨文件系统,因为:
硬链接的本质是多个目录项共享同一个 inode。

  • inode 编号只在同一个文件系统内唯一有效

  • 不同文件系统有各自独立的 inode 编号体系

  • 就像北京身份证号(110101...)在上海无效一样

创建硬链接

复制代码
ln 目标 链接名//创建硬链接

第一个参数已存在的目标文件;第二个参数要创建的新链接的名称

  • 硬连接不是独立的文件,没有独立的inode,本质上是在新增目录项,在特定目录的数据块中新增文件名和指向文件的inode编号的映射关系,即为文件取别名

  • 任意一个文件的inode内部都有一个叫做引用计数的计数器,表示有多少个文件名指向文件

    unlink 链接名
    rm 链接名

  • unlink 链接名 和 rm 链接名 都可以安全地删除一个硬链接,删除的实质是减少inode中的引用计数,当引用计数为零时才真正开始删除文件的流程

  • 硬链接数就是引用计数,它准确记录了有多少个不同的文件名(目录项)指向同一个 inode

删除目录时,该目录自身的链接数减少2(分别因父目录引用和自身.的移除),其父目录的链接数减少1(因子目录..的移除

硬链接应用: 目录的 . 与 ..

  1. .(当前目录)

    它是一个指向该目录自身 inode 的硬链接。

    创建目录时,系统会自动在其数据块中创建一个名为 . 的目录项,其 inode 编号指向目录自己的 inode。

    作用:提供对当前目录自身的引用,常用于相对路径的起点(如 ./file)。

..(父目录)

它是一个指向该目录的父目录 inode 的硬链接。

创建目录时,系统会自动在其数据块中创建一个名为 .. 的目录项,其 inode 编号指向其父目录的 inode。

作用:提供返回上级目录的引用,用于导航路径(如 cd ..)。

目录的 . 和 .. 是硬链接的经典应用,是文件系统树形结构得以构建和导航的基础

  • 构建树形结构 : 每个目录的 . 都硬链接到自身的 inode。这标识了节点自身 每个目录的 .. 都硬链接到其父目录的 inode。这就像为每个节点(目录)都明确标注了"我的父亲是谁" 通过这种设计,目录之间建立了父子关系,形成了有层次的树
  • 实现路径导航 : 命令 cd ..:直接利用当前目录的 .. 条目,找到父目录的 inode,完成切换
    .. 是相对路径能在树中"向上"回溯的唯一依据。
  1. 用户不能给目录创建硬链接,否则会在路径解析和目录遍历过程中引发环路问题

    假设以下操作被允许(实际不允许)

    mkdir -p /home/user/dir1/subdir
    ln -d /home/user/dir1 /home/user/dir1/subdir/link_to_parent

    /home/user/dir1/
    ├── subdir/
    │ └── link_to_parent -> ../.. (硬链接,指向 dir1 自身)

    产生环路:dir1 → subdir → link_to_parent → dir1(回到自身)

  2. .. 解析歧义

    路径 /home/user/dir1/subdir/link_to_parent/.. 应该指向哪里?

    按照路径解析:它应该指向 link_to_parent 的父目录,即 subdir。

    但 link_to_parent 是 dir1 的硬链接,其父目录实际上是 /home/user。
    系统无法决定应遵循目录树结构(subdir)还是硬链接关系(/home/user)。

遍历死循环:

程序遍历目录树时(如 find / -type f),进入 link_to_parent 后会再次进入 dir1,形成无限循环。

需要额外的复杂机制(如记录已访问的inode)来检测环路,这会增加开销和复杂性。

系统程序(如 ls、find)在遍历目录时,会显式忽略 . 和 .. 这两个条目
3.

文件删除困境:

目录的删除条件是其内容为空。
在环路中,dir1 的内容包含 subdir,subdir 的内容包含指向 dir1 的硬链接。两者互相引用,导致引用计数永不为0 ,形成逻辑上的"死锁",两个目录都无法被删除。

相关推荐
物理与数学2 小时前
Linux 内核 LRU 页面置换算法
linux·linux内核
小白同学_C3 小时前
Lab1-Xv6 and Unix utilities 配置环境的搭建以及前言 && MIT6.1810操作系统工程【持续更新】
linux·c/c++·操作系统os
haluhalu.3 小时前
深入理解Linux线程机制:线程概念,内存管理
java·linux·运维
乙酸氧铍3 小时前
【imx6ul 学习笔记】Docker 运行百问网 imx6ul_qemu
linux·docker·arm·qemu·imx6ul
不会C++的雾3 小时前
Linux操作系统(2)
linux·数据库·mysql
Code-world-14 小时前
NVIDIA Isaac Sim 安装教程
linux·人工智能·ubuntu·强化学习·isaac sim
cui__OaO4 小时前
Linux驱动--驱动编译
linux·运维·服务器
SunnyRivers4 小时前
深入理解Linux后台命令
linux·后台运行·重定向·nohub
刘叨叨趣味运维4 小时前
快速掌握Linux启动过程:像看接力赛一样简单
linux