Linux-【文件系统下】

一、引入"inode"概念

文件 = 数据 + 属性 , 当我们使用ls -l 的时候看到了除了文件名 , 还能看到文件的元数据 (属性)

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

其实这个信息除了通过这种方式来读取 , 一个stat 命令能够查看更多信息

文件数据都存储在 "块" 中 , 那么很显然,我们还必须找到一个地方存储文件的元信息(属性信息),比如文件的创建者 、文件的创建日期 、 文件大小等等这种存储文件元信息的区域就叫做 inode , 中文译名 , "索引结点"

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

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

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

重点:

  • 文件名不存放在 inode 中(因为string的大小会浮动,导致inode结点大小改变)而是存放在目录文件的数据块里(目录项中)。这是 inode 设计的一个关键点:文件名和 inode 分离,使得硬链接成为可能。
  • inode 的大小一般是128字节或者256 , 我们后面统一 128字节 。这使得文件系统可以像数组一样管理 inode,通过 inode 号直接计算出它在磁盘上的位置。
  • 任何文件的内容大小可以不同,但是属性的大小一定是相同的

问题引出:

  1. 硬盘是典型的 "块" 设备 , 操作系统读取硬盘数据的时候 , 读取的基本单位是块 。 块又是硬盘的每个分区下的结构 , 难道 "块" 是随意在分区上排布的吗 ? 要如何找到块呢?

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

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

文件系统通过在分区中预先规划出块组 ,在块组中设立位图 来管理空闲状态,设立inode表 来集中存放属性,并利用inode内的指针数组来记录内容块的位置,从而将一盘散沙的LBA扇区,组织成了一个井然有序、可以通过路径和文件名快速存取数据的结构化空间。

二、ext2文件系统

  • 我们想要在硬盘上存储文件,必须先把硬盘格式化为某种格式的文件系统 ,才能存储文件
    • 裸硬盘本身只是一堆无序的物理扇区,没有任何 "文件" 的概念,操作系统无法直接在上面创建、读取或删除文件。
    • 格式化的本质:给硬盘分区写入一套固定的管理规则(文件系统),比如 Ext2/Ext3/Ext4、NTFS、FAT32 等。
    • 只有完成格式化,分区才会被划分成块、块组、inode 表等结构,操作系统才能用 "文件" 的方式去组织和访问数据。
  • 文件系统的目的就是组织和管理硬盘中的文件
    • 数据怎么存:把文件内容拆分成块,记录块的位置;
    • 属性怎么存:用 inode 存储文件的权限、大小、时间戳等元数据;
    • 资源怎么分配:用位图管理空闲块和 inode,避免数据覆盖;
    • 路径怎么解析:用目录文件维护 "文件名 → inode 号" 的映射,让用户能通过路径找到文件。

2.1 宏观认识

ext2 文件系统将整个分区划分成若干个同样大小的块组 (Block Group)只要能管理一个分区就能管理所有分区,也就能管理所有磁盘文件

上图中启动块(Boot Block/Sector)的大小是确定的,为1KB,由PC标准规定,用来存储磁盘分区信息和启动信息,任何文件系统都不能修改启动块。启动块之后才是ext2文件系统的开始。

2.2 Block Group

Ext2 文件系统将每个分区划分为若干个 Block Group,所有块组的结构完全相同 ,就像一个 "行政区",每个行政区独立管理自己的资源,整体又由超级块统一协调。

每个 Block Group 的结构从前往后依次为:启动块(Boot Block)→ 超级块(Super Block)→ 块组描述符表(GDT)→ 块位图(Block Bitmap)→ inode 位图(Inode Bitmap)→ inode 表(Inode Table)→ 数据块(Data Block)

2.3 块组内部构成

  • 块组的各个组件各司其职,共同完成文件的存储与管理,缺一不可。
  • inode 和 数据块 , 跨组编号的
  • inode和数据块,不能跨分区!!!(所以,一个分区内部,inode编号和块号是唯一的)

2.3.1 超级块(Suoer Block)

超级块存储整个分区的文件系统全局信息,是 Ext 文件系统的核心,若超级块损坏,整个文件系统将无法使用。其记录的关键信息包括:

  • Block 和 Inode 的总数量、空闲数量;
  • Block 和 Inode 的大小;
  • 文件系统的挂载时间、写入时间、磁盘检查时间;
  • 块组的数量、每个块组的 Block/Inode 数量。

为了保证可靠性,超级块会在多个块组中进行备份(第一个块组必须有),防止单个扇区物理损坏导致超级块丢失。

2.3.2 GDT(Group Descriptor Table)

GDT 包含多个块组描述符,一个块组对应一个描述符,记录每个块组的局部信息:

  • 块组内 Block Bitmap、Inode Bitmap、Inode Table 的起始块号;
  • 块组内空闲 Block、空闲 Inode 的数量;
  • 块组内已使用的目录数量。

GDT 同样会在多个块组中备份,与超级块配合实现文件系统的整体管理。

2.3.3 块位图(Block Bitmap)

位图采用位(bit) 作为单位,每一位对应一个 Block 或 Inode,用于快速标记资源的空闲 / 占用状态:

  • Block Bitmap :每一位对应一个 Data Block,0 表示空闲,1 表示已占用;

2.3.4 inode位图(Inode Bitmap)

  • Inode Bitmap每一位对应一个 Inode0 表示空闲,1 表示已占用。

位图的设计让文件系统能快速找到空闲的 Block/Inode,避免了遍历整个分区的低效操作。
举一个例子:

  • 删除电影 , 不需要把内容删掉 , 只需要把位图清0 ;
  • 对于磁盘来说,只要块 inode 没有,内容就是乱码,无效
  • 对于文件的恢复 : 把位图再置1

2.3.5 节点表(Inode Table)

在 Linux 中,文件 = 内容 + 属性 ,文件的内容存储在 Data Block 中,而文件的属性(元信息) 则存储在Inode(索引节点) 中,Inode 表就是块组内所有 Inode 的集合。

什么是 Inode?

Inode 是一个固定大小的结构体(通常 128/256 字节),每个文件对应唯一的一个 Inode,且 Inode 编号以分区为单位全局唯一,不可跨分区。Inode 中存储的文件属性包括:

  • 文件的权限、所有者、所属组;
  • 文件的大小、创建时间、访问时间、修改时间;
  • 文件占用的 Data Block 编号;
  • 文件的硬链接数。

重要注意文件名并不存储在 Inode 中,这是 Linux 文件系统的关键设计,也是软硬链接实现的基础。

2.3.6 Data Block

Data Block 是真正存储文件内容的区域,根据文件类型的不同,Data Block 的存储内容也不同:

  • 普通文件:直接存储文件的实际数据;
  • 目录文件 :存储文件名与 Inode 号的映射关系(这是目录的核心本质);
  • 设备文件 / 管道文件:无实际数据,Data Block 为空。

**Data Block 的编号以分区为单位全局唯一,不可跨分区,**文件占用的 Data Block 编号会记录在其对应的 Inode 中。

2.4 inode 和 datablock 映射(弱化)

Inode 中包含一个块指针数组 i_block [EXT2_N_BLOCKS] (EXT2_N_BLOCKS=15),**用于建立 Inode 与 Data Block 的映射关系,**通过这个数组,就能找到文件内容所在的所有 Data Block。

问:知道inode 号的情况下,在指定分区 , 解释 : 对文件的增 、 删 、 查 、 改 是在做什么?

  • 分区之后的格式化操作,就是对分区进行分组 , 在每个组中写入 SB 、GDT , Block bitmap等管理信息 , 这些管理信息统称为:文件系统
  • 只要知道文件的inode号 , 就能在指定分区中确定是哪一个分组 , 进而在哪一个分组中确定是哪一个inode
  • 拿到inode文件的属性和内容就全部都有了
    下面,通过touch一个新文件来看看如何工作。

第一步:用 inode 编号查 inode 位图(有效性校验)

  • 每个块组都有一个 inode 位图(inode bitmap) ,用 1 个 bit 标记对应 inode 是否被占用:
    • bit=1:这个 inode 有效,代表存在一个文件 / 目录;
    • bit=0:这个 inode 空闲,未被使用。
  • 拿到 inode 编号后,先去位图里查对应 bit:
    • 如果是 1 → 说明这个 inode 是合法存在的;
    • 如果是 0 → 说明这个文件已经被删除或不存在。

第二步:从 inode 表中读取 inode 结构体

  • 每个块组都有一个 inode 表(inode table),是一段连续的磁盘空间,里面按顺序存放所有 inode 结构体(每个 inode 大小固定,比如 128/256 字节)。

  • 计算偏移:

    复制代码
    inode 在 inode 表中的偏移 = (inode 编号 - 1) × 每个 inode 的大小
  • 直接读取这段偏移的数据,就能得到完整的 struct ext2_inode,里面包含了文件的所有元信息:权限、大小、时间戳、数据块指针等。

第三步:通过 inode 里的 data block 数组找到文件内容

  • struct ext2_inode 里有一个数组 i_block[EXT2_N_BLOCKS](通常是 15 个指针):
    • 前 12 个是直接块指针 :直接指向存储文件内容的 data block 编号
    • 后 3 个是间接块指针:指向索引块,索引块里再存 data block 编号,用来存大文件。
  • 只要遍历这个数组,就能拿到文件所有 data block 的编号,再根据块号 → LBA 地址 → 磁盘扇区,最终读出文件的全部内容。

2.5 目录与文件名

我们平时访问文件时,使用的是路径 + 文件名 (如 /home/whb/test.c),但 Linux 的文件系统核心是 Inode,并不直接识别文件名,那么从 "文件名" 到 "文件内容 / 属性" 的过程是如何实现的?这就涉及到目录的本质路径解析路径缓存

目录的本质:文件名与 Inode 的映射文件

在 Linux 中,目录也是一种文件,没有特殊的 "目录结构",其本质是:

  • 目录的属性:存储在其对应的 Inode 中(与普通文件一致);
  • 目录的内容 :存储在其对应的 Data Block 中,内容为一系列 "文件名 - Inode 号" 的键值对。(所以在一个目录中,文件名不可以重复)

简单来说,**目录就是一个 "映射表",**建立了文件名和 Inode 号的关联,这也是为什么删除文件名只是删除目录中的一条映射记录,而不是直接删除文件数据。

2.6 路径解析

问题:打开当前工作目录文件 , 查看当前工作目录文件的内容 ? 当前工作目录不也是文件吗 ? 我们访问当前工作目录不也是只知道当前工作目录的文件名吗 ? 要访问它 , 不也要知道当前工作目录的inode ?

所以也要打开 , 当前工作目录的 上一级 目录 , ... , 上一级目录也是目录 !所以类似 "递归" , 需要把路径中所有目录全部解析 , 出口是 '/' 根目录 。

任何文件 , 都有路径 , 访问目标文件 ,例如:

以访问/home/whb/test.c为例,路径解析的步骤为:

  1. 系统开机后默认知道根目录 "/" 的 Inode 号(固定值),先打开根目录的 Inode,找到其对应的 Data Block;
  2. 在根目录的 Data Block 中,查找 "home" 对应的 Inode 号,打开 home 目录的 Inode;
  3. 在 home 目录的 Data Block 中,查找 "whb" 对应的 Inode 号,打开 whb 目录的 Inode;
  4. 在 whb 目录的 Data Block 中,查找 "test.c" 对应的 Inode 号,找到后即可通过该 Inode 获取文件的属性和内容所在的 Data Block。

核心结论 :访问文件必须依赖路径 ,**路径的本质是逐层的目录映射表,**最终通过文件名找到 Inode 号,进而访问文件。

所以,这个用户提供文件名 , 进程提供cwd , 共同构建了linux的路径结构

2.7 路径缓存

如果每次访问文件都要从根目录开始逐层解析磁盘上的目录,效率会非常低。因此,Linux 内核引入了路径缓存(dentry 缓存) ,在内存中(只在OS中保存,不存在磁盘里)维护了一棵树形的目录结构 ,由**内核结构体struct dentry(目录项)**构成。

  • 每个文件 / 目录都对应一个 dentry 结构,包含其 Inode 指针、父目录 dentry 指针、子目录 dentry 链表;
  • 当首次访问某个路径时,内核会从磁盘加载目录信息,在内存中创建 dentry 结构,形成树形缓存;
  • 后续再次访问该路径时,直接从内存的 dentry 缓存中查找,无需再访问磁盘,大幅提升效率。

dentry 缓存采用LRU(最近最少使用) 机制进行淘汰,同时结合哈希表实现快速查找,平衡了缓存效率和内存占用。

2.8 挂载分区

Inode 和 Block 的编号都是以分区为单位 的,跨分区后 Inode 号会重复,那么 Linux 如何管理多个分区的文件系统?答案是挂载(mount)

挂载的本质

将一个格式化好的分区 与一个空目录 建立关联,这个目录称为挂载点 ,此后访问该挂载点,就是访问对应分区的文件系统。简单来说,挂载就是将分区的文件系统 "接入" Linux 的根目录树形结构中

以挂载一个 ext4 格式的磁盘镜像文件为例:

复制代码
# 创建磁盘镜像(5M)
dd if=/dev/zero of=./disk.img bs=1M count=5
# 格式化为ext4文件系统
mkfs.ext4 disk.img
# 创建挂载点
mkdir /mnt/mydisk
# 挂载分区到挂载点
sudo mount -t ext4 ./disk.img /mnt/mydisk/
# 卸载分区
sudo umount /mnt/mydisk

关键结论

  • 未挂载的分区,即使格式化了文件系统,也无法被访问;
  • 访问文件时,系统会根据路径前缀判断目标文件所在的分区(挂载点),进而在对应分区中解析 Inode;
  • Linux 的根目录树形结构,本质是多个挂载的分区通过挂载点连接而成的整体。
  • 所以 , 可以根据访问目标文件的 "路径前缀" 准确判断我在哪一个分区!!!

2.9 文件系统总结

三、软硬链接

基于 "文件名与 Inode 分离" 的设计,Linux 实现了文件链接 机制,分为硬链接(Hard Link)软链接(Symbolic Link),两者的实现原理和使用场景截然不同,是 Linux 文件系统的重要特性。

3.1 硬链接

硬链接的本质是在目录中添加一条新的 "文件名 - Inode 号" 映射记录 ,让多个文件名指向同一个 Inode,这些文件名就是该文件的硬链接。

复制代码
# 创建文件abc
touch abc
# 为abc创建硬链接def
ln abc def
# 查看Inode号(两者Inode号相同)
ls -li abc def
# 输出:263466 abc  263466 def

硬链接的核心特性

  1. 硬链接与原文件共享同一个 Inode,拥有相同的文件属性和内容,修改其中一个,另一个也会同步变化;

  2. Inode 中记录了硬链接数,创建硬链接时链接数 + 1,删除硬链接时链接数 - 1;

  3. 只有当硬链接数为 0 时,系统才会真正删除文件的 Inode 和 Data Block,释放磁盘空间;

  4. 硬链接不能跨分区 (Inode 号仅在分区内唯一

    复制代码
    # 为abc创建软链接abc.s
    ln -s abc abc.s
    # 查看Inode号(软链接有独立的Inode号)
    ls -li
    # 输出:263563 abc  2631678 lrwxrwxrwx 1 root root 3 abc.s -> abc

    );

  5. 硬链接不能指向目录(避免目录树形结构出现循环,导致路径解析死循环)。

硬链接的特殊应用

Linux 中的 .(当前目录) 和..(上级目录) 就是硬链接:

  • 每个目录的硬链接数至少为 2(自身的. + 父目录中的映射记录);
  • 若目录有子目录,子目录的..会让该目录的硬链接数 + 1。

3.2 软连接

链接(也叫符号链接)的本质是一个独立的文件有自己的 Inode 和 Data Block, 其 Data Block 中存储的内容不是实际数据,而是目标文件的路径 + 文件名相当于 Windows 中的 "快捷方式"。

复制代码
# 为abc创建软链接abc.s
ln -s abc abc.s
# 查看Inode号(软链接有独立的Inode号)
ls -li
# 输出:263563 abc  2631678 lrwxrwxrwx 1 root root 3 abc.s -> abc

软链接的文件权限位以l开头,明确标识其为软链接,箭头指向目标文件。

软链接的核心特性

  1. 软链接是独立文件,有自己的 Inode 和 Data Block,与目标文件的 Inode 无关;
  2. 删除软链接 ,不会影响目标文件;删除目标文件,软链接会变成 "死链接"(指向不存在的文件);
  3. 软链接可以跨分区 ,也可以指向目录
  4. 访问软链接时,系统会根据其 Data Block 中存储的路径,自动跳转到目标文件,实现间接访问。

3.3 软硬链接对比

特性 硬链接 软链接
Inode 号 与目标文件相同 有独立的 Inode 号
本质 目录的映射记录 独立的文件
跨分区 不支持 支持
指向目录 不支持 支持
目标文件删除 仍可访问(链接数 > 0) 变成死链接
存储内容 无独立内容,共享数据 存储目标文件的路径

3.4 软硬链接的用途

  • 硬链接 :用于文件备份(删除原文件仍可通过硬链接访问)、实现...目录标识;
  • 软链接:用于简化文件路径(如将深层目录的文件链接到根目录)、版本管理(如将 python3 链接到 python)、跨分区访问文件 / 目录。
相关推荐
勇闯逆流河2 小时前
【Linux】linux进程概念(冯洛伊曼体系、操作系统、进程详解)
linux·运维·服务器
姓刘的哦2 小时前
RK3568之热插拔
linux
Penguido2 小时前
解决 VS Code 中 Git 推送报错:ECONNREFUSED vscode-git.sock 与鉴权失败
linux·git·vscode
Han.miracle2 小时前
Lombok 构造相关核心注解全解析
java·linux·算法
Java面试题总结2 小时前
2026最新Java八股文(完整版)
java·开发语言·jvm·数据库·java面试·java八股文
新缸中之脑2 小时前
68个适合个人GPU部署的LLM
数据库
爱丽_2 小时前
Linux 安装 MySQL 与远程连接排障(yum 方案)
linux·运维·mysql
IT WorryFree2 小时前
OpenClaw的运维命令
运维
麦聪聊数据2 小时前
SQL2API 网关的透明缓存与请求合并机制
数据库·sql·低代码·微服务