【Linux】Ext 系列文件系统深度解析:从 LBA、inode、块组到路径解析、挂载与软硬链接

📅 2026/8/16 13:18:41
【Linux】Ext 系列文件系统深度解析:从 LBA、inode、块组到路径解析、挂载与软硬链接
本文定位面向已经掌握 Linux 基础命令、准备深入文件系统与系统编程的同学沿着“块设备 → Ext 布局 → inode → 目录项 → 路径解析 → 挂载 → 链接 → 日志”这条主线建立一套能真正解释现象的文件系统模型。学习目标不仅知道 Ext2、Ext3、Ext4 的名词还能回答文件名为什么不在 inode 中、删除文件后空间为何可能不立即释放、挂载为何会遮蔽原目录内容、硬链接为何不能跨文件系统以及 Ext4 为什么更常使用 extent 而不是三级间接块。文章目录前言先把“名字、元数据、数据”分开一、从机械磁盘到 LBA二、扇区、文件系统块、内存页与分区三、inode文件元数据的核心四、Ext 块组把大文件系统拆成局部管理单元五、inode 如何定位文件数据六、文件创建、读取与删除的完整生命周期七、目录与路径解析八、挂载把文件系统接入目录树九、硬链接与符号链接十、Ext3/Ext4 日志与崩溃恢复十一、Ext2、Ext3、Ext4 的演进十二、动手实验用镜像文件安全观察 Ext4十三、常见误区与高频面试题总结前言先把“名字、元数据、数据”分开用户看到的是一个路径/home/alice/docs/report.txtExt 文件系统内部看到的却不是一个“带名字的文件对象”而是一组协作关系父目录中的目录项 report.txt → inode number ↓ inode 类型 / 权限 / UID / GID / 大小 时间戳 / link count / 数据映射 ↓ extent 或间接块索引 ↓ 数据块这三个对象必须先分开对象主要保存什么是否保存普通文件名目录项directory entry名字、inode 编号Ext4 中还可带文件类型保存名字inode类型、权限、属主、大小、时间戳、链接计数、数据映射等通常不保存普通文件名数据块普通文件内容、目录项、索引块、扩展属性等取决于数据用途一句话记忆目录项负责“名字指向谁”inode 负责“这个对象是什么”数据块负责“内容在哪里”。这套模型能直接解释硬链接、重命名、删除后延迟回收、路径解析和挂载等问题。一、从机械磁盘到 LBA1.1 机械硬盘的经典结构机械硬盘的教学模型通常包含盘片表面记录数据主轴带动盘片旋转磁头读写盘面磁臂把磁头移动到目标半径磁道盘面上的同心圆扇区磁道划分出的区域柱面不同盘面上相同半径磁道组成的逻辑集合。机械盘一次访问的时间主要来自寻道时间磁臂移动到目标磁道旋转延迟等待目标扇区转到磁头下传输时间真正读写数据。因此文件系统会尽量让相关的元数据和文件数据靠近也会通过缓存、预读、合并 I/O 等方式减少随机访问。注意Ext 的很多经典布局设计都能在机械盘背景下理解但文件系统并不只运行在机械硬盘上。SSD、虚拟块设备、RAID、LVM 和 loop 设备同样可以承载 Ext 文件系统。1.2 CHS经典教学坐标CHS 使用三个坐标定位扇区CCylinder柱面号HHead磁头号SSector扇区号。在“每磁道扇区数固定”的简化模型中设磁头数为heads每磁道扇区数为sectors_per_trackLBA C × heads × sectors_per_track H × sectors_per_track (S - 1)这里的S - 1来自经典 CHS 扇区号通常从 1 开始而 LBA 从 0 开始。反向换算为C LBA / (heads × sectors_per_track) H (LBA % (heads × sectors_per_track)) / sectors_per_track S (LBA % sectors_per_track) 1这些公式适合帮助理解地址线性化但不要把它们当成现代磁盘内部真实几何结构的完整描述。1.3 LBA系统真正关心的一维地址现代块设备通常向操作系统暴露 LBALogical Block Address[0][1][2][3][4][5] ... [n]操作系统提交一个线性编号设备固件再负责内部映射、坏块替换、闪存转换层或其他实现细节。LBA 隐藏了真实介质的复杂性也让 HDD、SSD、虚拟磁盘等设备可以使用相似的块接口。二、扇区、文件系统块、内存页与分区2.1 四个容易混淆的单位概念所属层次作用常见值示例逻辑扇区块设备接口操作系统看到的设备寻址单位512 B、4 KiB物理扇区设备介质设备实际读改写的物理单元4 KiB文件系统块Ext 文件系统分配、记录和组织空间的基本单位常见 4 KiB内存页虚拟内存页表、缺页和页缓存管理单位常见 4 KiB它们的数值可能恰好相同但不能画等号因为它们来自不同层次、承担不同职责。例如逻辑扇区为 512 B、文件系统块为 4 KiB 时一个文件系统块覆盖 8 个逻辑扇区文件系统块号 LBA / 8 块内逻辑扇区偏移 LBA % 8Ext 的块大小记录在超级块中内核文档给出的计算方式是block_size 2 ^ (10 s_log_block_size)所以s_log_block_size 2时块大小为 4 KiB。Ext4 启用bigalloc后还会在文件系统块之上引入更大的 cluster 分配单位此时块位图实际按 cluster 跟踪空间。本文主体先以未启用bigalloc的常见配置讲解。2.2 分区、格式化和挂载不是一回事三者经常被一句“把磁盘分区格式化后挂载”连在一起实际是三个独立动作整块磁盘 ↓ 分区划分 LBA 地址范围 分区 / 逻辑卷 / 其他块设备 ↓ 格式化写入 Ext 超级块、位图、inode 表等 一个可识别的文件系统 ↓ 挂载接入 Linux 目录树 用户可通过路径访问分区只是在分区表中描述一段地址范围格式化在块设备上创建某种文件系统挂载让一个已有文件系统出现在指定目录路径下。Ext 可以建立在分区上也可以建立在整块设备、LVM 逻辑卷、RAID 设备或普通镜像文件对应的 loop 设备上。常用只读观察命令lsblk-ffindmnt blkiddf-hTdf-i⚠️mkfs.ext4、fdisk、parted等命令会改变磁盘结构或文件系统。不要把教程中的目标路径替换成保存重要数据的/dev/sdX、/dev/nvme...或其他真实设备。三、inode文件元数据的核心3.1 inode 保存什么inode 是 Ext 文件系统描述文件对象的核心结构。典型字段包括文件类型和权限位UID、GID文件大小atime、mtime、ctimeExt4 还可保存 crtime硬链接计数数据块映射或 extent treeinode 标志、扩展属性、校验信息等。查看文件的 inode 编号与元数据ls-lidemo.txtstatdemo.txtstat-cinode%i links%h size%s blocks%bdemo.txtinode 编号的作用域是单个文件系统。两个不同文件系统都可以存在 inode 3017它们并不是同一个对象。这正是硬链接不能跨文件系统的根本原因。3.2 inode 不保存普通文件名普通文件名存在于父目录的数据中目录项demo.txt → inode 3017inode 3017 本身并不知道哪个目录项叫demo.txt。同一个 inode 可以被多个目录项引用于是形成硬链接。这也解释了为什么同一文件系统内的重命名通常很快核心动作主要是修改目录项而不是搬运整份文件数据。3.3 四个时间戳不要混淆时间含义典型变化场景atime最近访问时间读取文件实际更新受relatime、noatime等挂载策略影响mtime文件数据最近修改时间写入、截断文件内容ctimeinode 状态最近变化时间权限、属主、链接数、mtime 等状态变化btime / crtime创建时间文件系统和接口支持时可读取最常见的误区是把ctime解释成 creation time。它实际是change time即 inode 状态变化时间。Ext4 的创建时间字段在磁盘格式中称crtime用户态可能通过statx()或stat的 Birth 字段看到它但是否可用仍取决于内核、文件系统和工具支持。3.4 inode 大小不是永远固定经典 Ext2/Ext3 inode 记录是 128 BExt4 允许在格式化时选择更大的 inode 记录常见默认值为 256 B。实际大小保存在超级块的s_inode_size中。更大的 inode 可以容纳纳秒级扩展时间戳、创建时间、项目 ID、校验值和部分扩展属性等信息。因此不能把“inode 永远是 128 字节”当成通用结论。3.5 inode 号如何定位到 inode 表设每个块组包含s_inodes_per_group个 inode目标 inode 编号为inoblock_group (ino - 1) / s_inodes_per_group index (ino - 1) % s_inodes_per_group byte_offset index × s_inode_sizeinode 0 被定义为不存在所以计算时需要先减 1。找到块组后再通过块组描述符定位该组的 inode table。四、Ext 块组把大文件系统拆成局部管理单元4.1 为什么需要 Block Group如果整个大文件系统只使用一份巨大的 inode 表和位图不仅管理范围过大目录、inode 与文件数据也可能相距很远。Ext 把文件系统划分为多个 Block GroupExt 文件系统 ├── Block Group 0 ├── Block Group 1 ├── Block Group 2 └── ...每个组管理一部分 inode 和块。分配器会尽量让相关对象保持邻近例如让一个目录下的文件分布在合适的局部区域以改善访问局部性。4.2 块组的典型组成教学模型中一个块组常画成[Superblock/GDT 备份可选] [Block Bitmap] [Inode Bitmap] [Inode Table] [Data / Index Blocks]每一部分的职责如下结构作用Superblock保存整个文件系统的全局参数、特性标志、总量和状态等Group Descriptor Table记录各块组位图、inode 表位置和空闲统计等Block Bitmap标记块是否分配bigalloc下按 cluster 管理Inode Bitmap标记 inode 槽位是否使用Inode Table保存该组的 inode 记录Data / Index Blocks保存文件数据、目录项、extent/间接索引、扩展属性等4.3 超级块偏移的准确理解Ext 文件系统的主超级块从文件系统起始位置偏移 1024 B 处开始。对块组 0 来说最前面的 1024 B 被保留历史上可用于 x86 引导扇区等用途块大小为 1 KiB 时超级块位于块 1块大小大于 1 KiB 时超级块位于块 0 内偏移 1024 B 的位置。这不等于“每个分区前面都有一个文件系统永远不能修改的 1 KiB boot block”也不应与 GPT/MBR 分区表混为一谈。这里讨论的是Ext 文件系统内部相对于文件系统起点的布局。4.4 备份不是每个块组都有布局也并非绝对固定常见示意图会把每个块组都画成相同顺序但真实 Ext4 文件系统可能启用sparse_super/sparse_super2只在部分块组保存超级块和 GDT 备份flex_bg把多个块组组合成 flexible block group让位图与 inode table 集中放置meta_bg改变块组描述符的分布方式以支持更大的文件系统uninit_bg延迟初始化部分位图或 inode table缩短格式化时间。因此块组图是理解职责的概念图不是可以对所有 Ext4 镜像硬套的固定偏移表。真实位置应读取超级块特性和块组描述符。4.5 “空间还有很多却不能创建文件”创建普通文件至少需要一个可用 inode写入数据还需要可用块。两类资源要分开观察df-h# 数据空间df-i# inode 使用率大量极小文件可能先耗尽 inode而不是先耗尽字节容量。此时df -h看起来仍有空间却会出现No space left on device。五、inode 如何定位文件数据5.1 Ext2 的直接块与三级间接块经典 Ext2 inode 的i_block区域包含 15 个块号槽位i_block[0..11] → 12 个直接块指针 i_block[12] → 一级间接块 i_block[13] → 二级间接块 i_block[14] → 三级间接块若文件系统块大小为B每个块号占 4 B则一个索引块可容纳N B / 4理论寻址容量为direct 12 × B single N × B double N² × B triple N³ × B以 4 KiB 块为例N 1024层级理论覆盖范围12 个直接块48 KiB一级间接4 MiB二级间接4 GiB三级间接4 TiB这里是索引结构的理论地址跨度不等于具体内核与磁盘格式真正支持的最大文件大小。实际限制还会受到块计数字段、实现和文件系统配置影响。5.2 Ext4 为什么改用 extent大文件往往包含很长的连续块范围。如果仍逐块保存块号索引元数据会非常庞大。Extent 用三个核心量描述一段连续空间逻辑块起点 → 物理块起点 连续长度例如LBN 0–127 → PBN 8000–8127 128 blocks LBN 128–319 → PBN 9400–9591 192 blocks一个 extent 就能代表一串连续块。Ext4 的inode.i_block前 60 B 可以保存 extent tree 的头部与根节点树变大后再引用外部 extent 节点块。5.3 Ext4 不只是“Ext2 加了日志”Ext4 的常见空间管理特性还包括extent treedelayed allocation延迟决定物理块位置multiblock allocator一次分配连续范围64-bit 块号与更大文件系统支持flexible block groups元数据校验和inline data在条件满足时把小数据放入 inode 区域。小符号链接也常采用“fast symlink”方式把路径直接放进 inode 的i_block空间从而不必额外分配数据块。5.4 稀疏文件与st_blocks文件的逻辑大小不一定等于实际占用空间truncate-s1G sparse.binstat-csize%s bytes, allocated%b × 512Bsparse.bindu-hsparse.bindu-h--apparent-size sparse.binst_size描述逻辑字节长度st_blocks在常见 Linux 接口中以 512 B 单位报告已分配块数它不是 Ext 文件系统块的数量。稀疏文件未实际写入的洞不会占用对应数据块读取时由文件系统返回零。六、文件创建、读取与删除的完整生命周期6.1 创建文件以在某目录创建hello.txt为例逻辑过程可以概括为检查父目录权限和目标名字是否已存在在合适块组的 inode bitmap 中寻找空闲 inode初始化 inode 的类型、权限、UID/GID、时间戳和链接计数在父目录数据中增加hello.txt → inode number目录项写入内容时再分配数据块或建立 extent更新位图、块组统计、inode 大小和时间戳Ext3/Ext4 根据日志和写回策略提交相应元数据。Ext4 的 delayed allocation 意味着“用户已经 write”与“最终物理块位置已经确定”不一定发生在同一时刻。内核可以等积累更多数据后再做更连续的分配。6.2 读取文件一次基于路径的读取大致经历路径字符串 ↓ 逐级查询目录项 / dentry cache ↓ 得到目标 inode ↓ 检查类型、权限与打开标志 ↓ 把文件逻辑偏移转换为逻辑块 ↓ 通过 extent tree 或间接块定位物理块 ↓ 先查 page cache未命中时提交块 I/O ↓ 数据复制到用户缓冲区这里还会经过 VFS、具体 Ext4 实现、页缓存和块层真实调用链比示意图更长但核心对象关系不变。6.3unlink()删除的是名字unlink(hello.txt)首先移除的是父目录里的目录项并让 inode 的硬链接计数减 1。只有同时满足硬链接计数 0 并且 没有打开引用仍持有该文件inode 和数据块才可以真正回收。因此文件名已经消失时仍持有 fd 的进程可以继续读写该文件。常见现象包括服务日志已被rm但进程仍占用大量磁盘空间临时文件创建后立刻 unlink进程退出前仍可安全使用通过/proc/pid/fd/能看到带(deleted)的打开文件。查找“已删除但仍被进程打开”的文件sudolsofL1不要通过盲目删除/proc中的对象处理问题。通常应让持有者正确关闭 fd或按服务的日志轮转方式重新打开日志。七、目录与路径解析7.1 目录也是文件在 Ext 中目录的数据保存一组目录项。Ext4 常见目录项结构包含inode # 目标 inode 编号 rec_len # 当前目录项记录长度 name_len # 文件名长度 file_type # 文件类型提示 name # 文件名字节串目录项至少回答“这个名字指向哪个 inode”。文件的权限、大小和时间戳仍应到 inode 中读取。7.2 目录项和 dentry 不是同一个东西这是一个很容易被略过、但非常关键的区别名称在哪里作用Ext directory entry磁盘上的目录文件数据持久化name → inode number映射VFS dentry内核内存表示和缓存一次路径分量解析结果inode cache内核内存缓存内存中的 inode 对象page cache内核内存缓存文件数据页dentry cache 不只是“所有已打开文件组成的一棵树”。它可以保存正 dentry名字成功对应某个 inode负 dentry曾查询过但这个名字不存在。负 dentry 能避免程序反复查询不存在的路径时每次都访问磁盘目录数据。7.3 绝对路径与相对路径从哪里开始绝对路径以/开头从进程看到的根目录开始普通相对路径从当前工作目录cwd开始openat()等接口还能让相对路径从指定dirfd开始。这里的“进程根目录”处于挂载命名空间和进程根视图中不一定等于宿主机全局意义上的根。例如容器中的/可以是隔离后的文件系统视图。7.4 逐级解析时发生什么解析/home/alice/docs/a.txt时可抽象为/ 的目录项中查 home → home inode 的目录项中查 alice → alice inode 的目录项中查 docs → docs inode 的目录项中查 a.txt → 得到目标 inode每个中间目录都需要搜索权限即目录的x权限。只有对最终文件执行具体操作时才结合目标 inode 权限、ACL、打开方式和其他安全机制继续判断。解析过程中还可能遇到.与..符号链接需要按目标路径继续解析挂载点需要切换到另一个文件系统的根mount namespace、chroot、容器根等视图边界并发重命名、删除与缓存失效。7.5 用 C 读取目录项下面的程序使用opendir()/readdir()读取目录。d_ino是目录项中报告的 inode 编号d_type在某些文件系统上可能是DT_UNKNOWN此时不能只依赖它判断类型应继续使用fstatat()等接口。#includedirent.h#includeerrno.h#includeinttypes.h#includestdio.h#includestdlib.hintmain(intargc,char*argv[]){constchar*pathargc1?argv[1]:.;DIR*diropendir(path);if(dirNULL){perror(opendir);returnEXIT_FAILURE;}for(;;){errno0;structdirent*entryreaddir(dir);if(entryNULL){if(errno!0){perror(readdir);closedir(dir);returnEXIT_FAILURE;}break;}printf(inode%10ju d_type%3u name%s\n,(uintmax_t)entry-d_ino,(unsigned)entry-d_type,entry-d_name);}if(closedir(dir)-1){perror(closedir);returnEXIT_FAILURE;}returnEXIT_SUCCESS;}编译运行cc-Wall-Wextra-O2readdir_demo.c-oreaddir_demo ./readdir_demo /tmp八、挂载把文件系统接入目录树8.1 挂载改变的是路径解析接入点假设根文件系统中原本存在/mnt/demo/local.txt现在把另一个 Ext4 文件系统挂载到/mnt/demosudomount/dev/example /mnt/demo之后访问/mnt/demo时路径解析会跨入被挂载文件系统的根目录。原来的local.txt并未删除而是被挂载视图临时遮蔽卸载后它会重新可见。所以挂载不是把新文件复制进目录把两个目录的文件自动合并把文件系统永久绑定到一个固定路径。挂载更准确的理解是在当前挂载命名空间中把一个文件系统的根接到目录树的某个挂载点。8.2 挂载点必须是空目录吗技术上不要求为空但通常建议使用空目录。若目录本来有内容挂载期间这些内容会被遮蔽很容易造成“文件怎么突然没了”的误判。查看挂载关系findmnt findmnt--target/mnt/demo mountpoint /mnt/demo8.3 卸载失败target is busy常见原因包括某进程当前工作目录位于挂载点内部某文件仍被打开还有子挂载终端、服务或文件管理器仍在访问。可以先观察findmnt--target/mnt/demosudofuser-vm/mnt/demosudolsofD /mnt/demolsof D会递归扫描大目录上可能较慢。应先让相关进程退出目录或关闭文件再正常umount不要一上来就使用 lazy/force 卸载掩盖原因。8.4 挂载命名空间挂载关系不是所有进程都必须共享同一个全局视图。Linux mount namespace 可以让不同进程组看到不同的挂载树这是容器文件系统隔离的重要基础。同一路径字符串在两个命名空间中可能解析到不同文件系统对象。排查容器或服务中的路径问题时必须先确认“从哪个进程的挂载视图看”。九、硬链接与符号链接9.1 硬链接新的目录项指向同一 inodeprintfhello\na.txtlna.txt a.hardls-lia.txt a.hard逻辑关系是a.txt ───┐ ├──→ inode 3017 → 同一份数据 a.hard ───┘两个名字地位平等没有“原文件”和“快捷方式”的层级。删除任意一个目录项只会让 link count 减 1另一个名字仍然可以访问内容。硬链接的核心限制inode 编号只在单个文件系统内有意义因此不能跨文件系统普通用户不能随意给目录创建硬链接以免破坏目录树约束并制造环一个 inode 的所有硬链接共享内容和 inode 元数据但目录项名字各自独立。9.2 符号链接自己的 inode 保存目标路径ln-sa.txt a.symls-lia.txt a.sym readlink a.sym逻辑关系是a.sym 的目录项 ↓ 符号链接自己的 inode ↓ 内容是 a.txt 再次进行路径解析 ↓ 目标 a.txt符号链接可以跨文件系统指向目录在目标暂时不存在时先创建。目标被删除后符号链接自身仍存在只是访问目标时返回ENOENT形成 dangling symlink。9.3 相对符号链接从哪里解释这是实践中最容易出错的细节符号链接中的相对目标是相对于“符号链接所在目录”解释而不是相对于调用者当前工作目录解释。例如mkdir-pproject/releases/v1 project/currentln-s../releases/v1 project/current/latest readlink project/current/latest../releases/v1从project/current/出发解析到project/releases/v1。即使从其他工作目录访问该链接目标解释基准也不会改成调用者的 cwd。9.4 对比表对比项硬链接符号链接本质新目录项指向同一 inode独立 inode 保存路径字符串inode 是否相同相同不同能否跨文件系统不能可以能否指向目录通常受严格限制可以目标删除后其他硬链接仍可访问变为悬空链接是否增加目标 link count是否相对路径问题无相对链接所在目录解析十、Ext3/Ext4 日志与崩溃恢复10.1 为什么需要日志创建文件可能同时修改父目录的目录项inode bitmapblock bitmapinode table块组统计文件数据。如果写到一半突然断电磁盘上可能只完成了一部分更新。Ext3/Ext4 使用 jbd2 日志把相关元数据更新组织成事务崩溃恢复时根据完整提交记录重放或忽略未完成事务从而把文件系统恢复到可解释的一致状态。10.2 一次日志事务的概念流程产生元数据修改 ↓ 写入 journal descriptor 与日志中的元数据块 ↓ 写入 commit block事务完成 ↓ checkpoint把元数据写回最终位置 ↓ 日志空间可复用真实 jbd2 还包含 revoke、校验和、序列号、fast commit 等细节图中展示的是帮助理解的主线。10.3 三种 data 模式模式元数据文件数据主要特点dataordered写入日志不写入日志但相关数据要求先于元数据 commit 落到最终位置常见默认兼顾性能与合理一致性datawriteback写入日志不保证先于元数据 commit更弱的数据顺序保证datajournal写入日志也写入日志写放大更大语义更强ordered的“数据先行”不等于所有应用写入都已经获得持久化承诺也不能替代正确的fsync()/fdatasync()/ 目录同步设计。10.4 日志不等于备份日志主要解决文件系统元数据一致性和崩溃恢复问题它不能自动挽救用户误删应用把正确内容覆盖成错误内容勒索软件修改静默数据损坏整块设备丢失。这些问题仍然需要独立备份、快照、校验与恢复演练。十一、Ext2、Ext3、Ext4 的演进文件系统核心特点适合理解的关键词Ext2无日志经典块组、inode 与间接块结构基础磁盘格式、直接/间接块Ext3在 Ext2 兼容布局上加入 JBD 日志日志事务、较平滑升级Ext4extent、延迟分配、多块分配、校验和、更大规模等现代空间管理与可靠性增强11.1 Ext2Ext2 没有日志结构相对清晰非常适合学习 inode、位图、块组和间接块。不过异常断电后通常需要更完整的fsck扫描来恢复一致性。11.2 Ext3Ext3 的关键增强是加入日志能力磁盘格式与 Ext2 有较强兼容性。它让异常恢复通常可以通过重放日志快速完成而不是每次都扫描整个文件系统。11.3 Ext4Ext4 不只是容量变大还重做了大文件和连续空间管理extent 代替逐块映射作为常见路径delayed allocation 与 multiblock allocator 改善连续性flex_bg、meta_bg改变元数据布局与扩展能力metadata checksum 增强损坏检测64-bit 特性扩大块号和文件系统规模timestamp 扩展、inline data、目录索引等增强功能。理解 Ext2 的经典模型是基础但分析 Ext4 时必须继续读取文件系统特性标志不能把 Ext2 图原封不动地当成 Ext4 实际布局。十二、动手实验用镜像文件安全观察 Ext4⚠️ 以下实验只对新建的普通镜像文件执行mkfs.ext4。不要把变量改成真实磁盘或分区。挂载步骤需要sudo完成后务必卸载。12.1 创建独立实验目录和镜像demo_dir$(mktemp-d)image$demo_dir/ext4-demo.imgmount_dir$demo_dir/mnttruncate-s128M$imagemkfs.ext4-F$imagemkdir$mount_dirprintfdemo_dir%s\nimage%s\n$demo_dir$image这里的-F只因为目标是普通文件。执行前应确认$image位于刚创建的$demo_dir中。12.2 观察超级块与块组dumpe2fs-h$imagedebugfs-Rstats$image重点观察block sizeinode sizeblocks/inodes per groupfilesystem featuresjournal inodeflex_bg、metadata_csum、extent、64bit 等特性。列出每个块组的概要dumpe2fs$image|less12.3 通过 loop 挂载并创建文件sudomount-oloop,nosuid,nodev,noexec$image$mount_dirfindmnt--target$mount_dirprintfhello ext4\n|sudotee$mount_dir/hello.txt/dev/nullsudomkdir$mount_dir/docssudoln$mount_dir/hello.txt$mount_dir/docs/hello.hardsudoln-s../hello.txt$mount_dir/docs/hello.symls-li$mount_dir/hello.txt$mount_dir/docs/hello.hard$mount_dir/docs/hello.symsudoumount$mount_dir这里docs/hello.sym的内容是../hello.txt它从符号链接所在的docs/目录出发正确指向上一级的hello.txt。12.4 在未挂载镜像上查看 inode先从上一步ls -li记下hello.txt的 inode 编号例如 12debugfs-Rstat 12$imagedebugfs-Rblocks 12$image如果 inode 编号不是 12请替换成实际值。stat输出可观察 inode 类型、大小、链接计数、时间戳和 extentblocks会列出相关块号。12.5 验证“删除名字但打开 fd 仍可读”这个实验不需要挂载镜像printfstill alive through fd\n$demo_dir/open.txtexec3$demo_dir/open.txtrm$demo_dir/open.txtls-l/proc/$$/fd/3cat3exec3-rm后目录项已经不存在但当前 shell 的 fd 3 仍引用打开文件。关闭 fd 3 后如果硬链接计数为 0内核才能回收对应存储空间。12.6 清理实验确认已经卸载后再删除实验目录ifmountpoint-q$mount_dir;thenprintfstill mounted: %s\n$mount_dir2elserm-r--$demo_dirfi如果仍处于挂载状态应先正常执行sudo umount $mount_dir再次确认mountpoint -q返回未挂载后再清理不要直接删除挂载点。十三、常见误区与高频面试题13.1 文件名存在哪里普通文件名存放在父目录的目录项中目录项把名字映射到 inode 编号。inode 保存元数据与数据映射通常不保存普通文件名。13.2 inode 编号是不是整台机器全局唯一不是。inode 编号只在单个文件系统内唯一。唯一标识一个对象通常需要同时考虑设备/文件系统标识与 inode 编号。13.3ctime是创建时间吗不是。ctime是 inode status change time。创建时间通常称 btime 或 crtime是否可见取决于文件系统、内核和用户态接口。13.4 删除文件为什么空间没有立刻回来可能仍有其他硬链接也可能有进程持有打开 fd。只有链接数为 0 且没有打开引用时inode 和数据块才可回收。13.5 目录项、dentry 和 inode 有什么区别目录项磁盘中的名字到 inode 编号映射dentryVFS 在内存中表示/缓存路径分量inode文件对象的元数据与数据映射。13.6 为什么硬链接不能跨文件系统硬链接目录项只记录目标 inode 编号而 inode 编号只在当前文件系统内解释。跨文件系统后相同数字不能唯一定位原对象。13.7 软链接目标的相对路径从哪里解析从符号链接所在目录解析不是从访问者的 cwd 解析。13.8 挂载为什么会让目录原有文件“消失”原文件没有删除。挂载改变了该路径的解析接入点被挂载文件系统的根遮蔽了挂载点目录原有内容卸载后会重新可见。13.9 Ext4 是否仍使用直接、一级、二级、三级间接块经典 Ext2 模型使用这些指针。Ext4 普通文件通常启用 extent tree磁盘格式仍保留i_block区域但其中常保存 extent 头和树根而不是简单的 15 个传统块指针。13.10 日志能否防止用户误删不能。日志用于崩溃一致性恢复不是历史版本备份。误删与错误覆盖仍要依靠备份、快照和恢复策略。13.11df -h有空间为什么仍报 No space left on device除了数据块创建文件还需要 inode。先用df -i检查 inode 是否耗尽配额、保留块和其他限制也可能造成类似现象。13.12 为什么ls -l显示的文件大小和du不同ls -l主要显示逻辑大小du主要统计实际分配空间。稀疏文件、压缩、共享块和元数据策略都会让两者不同。总结把 Ext 文件系统串起来可以记住下面这条主线块设备暴露 LBA ↓ 分区或逻辑卷提供一段块地址空间 ↓ Ext 用块、块组、超级块、描述符和位图管理空间 ↓ 父目录用“名字 → inode 编号”定位对象 ↓ inode 保存元数据并通过间接块或 extent 定位数据 ↓ VFS 用 dentry/inode/page cache 加速路径与数据访问 ↓ 挂载把其他文件系统接入目录树 ↓ 硬链接共享 inode符号链接保存路径 ↓ Ext3/Ext4 用日志帮助恢复元数据一致性真正理解 Ext不是背一张磁盘布局图而是能在遇到现象时判断当前讨论的是设备扇区、文件系统块还是内存页文件名位于哪个父目录项路径解析从哪个 root、cwd 或 dirfd 开始inode 如何映射到数据块当前视图是否跨越挂载点或 mount namespace删除的是目录项还是对象已经满足回收条件沿着这些问题排查很多看似零散的文件系统现象都会回到同一套模型中。参考资料Linux Kernel DocumentationExt4 文件系统Linux Kernel DocumentationExt4 Block GroupsLinux Kernel DocumentationExt4 Index NodesLinux Kernel DocumentationThe Contents of inode.i_blockLinux Kernel DocumentationDirectory EntriesLinux Kernel DocumentationJournalLinux man-pagespath_resolution(7)Linux man-pagesinode(7)Linux man-pageslink(2)Linux man-pagessymlink(2)Linux man-pagesunlink(2)Linux man-pagesmount(8)推荐标签Linux文件系统Ext4inode操作系统系统编程