本博客深度整合磁盘物理结构、CHS/LBA寻址、磁盘逻辑抽象一维/二维/三维数组、inode原理、Ext块组架构、路径解析、挂载机制及软硬链接核心知识配合命令演示、C代码示例、对比表格与大量示意图真正做到“知其所以然”。适合Linux后端、运维、嵌入式、内核入门者也可直接作为面试复习纲要。前言在 Linux 世界里一切皆文件。支撑“文件”正常工作的底层基石就是Ext 系列文件系统。绝大多数 Linux 发行版Ubuntu、CentOS、Debian默认使用 Ext4它的前身是 Ext2、Ext3。很多同学只会用ls/cd/mkdir却不懂磁盘到底怎么存数据CHS 和 LBA 是什么关系磁盘如何从三维变成一维数组inode 是什么为什么删除文件还要看链接数目录到底是不是文件软链接和硬链接本质区别是什么挂载到底在做什么为什么有时候磁盘没满却提示无法创建文件这篇文章一次性把所有原理讲透。一、磁盘物理结构存储的最底层真相机械硬盘是计算机中唯一的机械设备相比内存虽然慢但容量大、价格便宜。1.1 核心部件盘片Platter涂有磁性材料双面均可存储数据。主轴Spindle带动盘片高速旋转。磁头Head每个盘面配一个磁头负责读写。所有磁头共进退。磁臂Actuator Arm驱动所有磁头同步移动。音圈电机精准控制磁臂定位。1.2 磁盘的存储结构扇区、磁道、柱面每个盘面被划分为一个个同心圆环称为磁道Track。磁道再被等分为扇区Sector。扇区磁盘读写的最小物理单元固定512 字节。磁道某一盘面上的同心圆所有盘面上相同半径的磁道在逻辑上构成一个柱面Cylinder。柱面分区的最小单位。单个盘片的磁道扇区视图:多个盘片组成的柱面视图:1.3 如何定位一个扇区—— CHS 寻址早期采用三维坐标定位扇区CCylinder柱面先找哪一个柱面HHead磁头再确定柱面内哪一个磁头即哪一个盘面/磁道SSector扇区最后定位该磁道上的第几个扇区注意扇区号从 1 开始CHS 寻址的上限约为8.4GB1024柱面 × 256磁头 × 63扇区 × 512字节。二、磁盘的逻辑结构从三维到一维的抽象重点2.1 理解思路像磁带一样拉直磁带可以存储数据把磁带“拉直”就形成线性结构。磁盘虽然是硬质的但逻辑上可以想象成卷在一起的磁带拉直后每个扇区就有了一个线性下标这就是LBALogical Block Address的由来。2.2 真实过程从三维数组到一维 LBA磁盘的真实物理结构可以这样逐层理解① 磁道 → 一维数组某一盘面的某一个磁道展开就是一条扇区序列就像一个一维数组每个元素是一个 512 字节的扇区。② 柱面 → 二维数组由于磁头共进退所有盘面上相同半径的磁道构成一个柱面。柱面上的每个磁道扇区个数相同所以一个柱面展开后就是一张二维的扇区数组表行 磁头/盘面列 扇区。③ 整盘 → 三维数组多个二维数组整个磁盘就是多个柱面柱面0、柱面1、柱面2…叠加即多张二维扇区数组表形成一个三维数组。第 n 个 柱 面…④ 最终抽象 → 一维 LBA 数组学过 C/C 数组就会知道三维数组在内存中本质上仍是一维的。同理磁盘最终被抽象为一个以扇区为元素的一维数组每个扇区有一个唯一的下标即LBA 地址。图示核心结论从此往后操作系统只认 LBA磁盘的固件负责将 LBA 自动转换为 CHS 去定位物理扇区。2.3 LBA ↔ CHS 转换公式# CHS → LBA扇区号 S 从 1 开始LBA 从 0 开始LBAC ×(磁头数 × 每磁道扇区数)H × 每磁道扇区数S-1# LBA → CHSCLBA//(磁头数 × 每磁道扇区数)H(LBA%(磁头数 × 每磁道扇区数))//每磁道扇区数 S(LBA%每磁道扇区数)1✅重点磁头数 × 每磁道扇区数 单个柱面的扇区总数这是换算中的关键中间量。三、从物理到逻辑块、分区与文件系统3.1 块Block文件系统的最小读写单位逐个扇区读写效率太低操作系统一次读写多个连续扇区这个逻辑单位称为块Block。常见大小4KB 8 个扇区格式化时确定不可更改文件存取、空间分配均以块为单位# 块号与 LBA 的简单换算以 4KB 块为例块号LBA//8LBA块号 ×8块内扇区偏移3.2 分区Partition磁盘可被划分为多个分区柱面是分区的最小单位。分区本质上就是划定起始和结束柱面每个区内的扇区分布就清晰了。Linux 下磁盘设备文件示例/dev/sda第一块 SCSI/SATA 磁盘/dev/sda1第一块磁盘的第一个分区图示3.3 格式化 创建文件系统所谓“格式化”就是向分区写入管理数据构建文件系统。后续所有文件的增删查改都在这个框架下进行。四、inode文件的真正灵魂核心重点4.1 核心思想Linux 文件 元信息inode 数据内容data block属性和内容分离存储。文件名不属于 inode每个文件有且只有一个 inodeinode 大小固定通常128 字节或256 字节任何文件的内容大小可以不同但属性大小一定相同。4.2 inode 里存了什么structext2_inode{__le16 i_mode;// 文件类型和权限__le16 i_uid;// 所有者用户ID__le32 i_size;// 文件大小字节__le32 i_atime;// 最后访问时间 (Access)__le32 i_ctime;// 属性变更时间 (Change)__le32 i_mtime;// 内容修改时间 (Modify)__le32 i_dtime;// 删除时间__le16 i_gid;// 组ID__le16 i_links_count;// 硬链接计数 ★★★__le32 i_blocks;// 文件占用的块数__le32 i_flags;// 文件标志__le32 i_block[15];// 数据块指针数组 ★★★};4.3 查看 inode 信息stattest.c# 查看完整 inode 元信息ls-litest.c# 仅查看 inode 号4.4 文件三个时间戳时间含义触发条件Accessatime最后访问时间读文件、执行文件Modifymtime内容修改时间写入文件内容Changectime属性变更时间权限、所有者变更甚至 mtime 变化也会触发五、多级索引inode 如何一步步找到数据块好的我给你一个精简、带高亮、复制到CSDN直接能用的版本。五、多级索引inode 如何找到数据块inode 里有个关键的数组叫i_block[15]一共 15 个指针分成两部分干活对照图片来看就两条链路上面那条12 个直接指针直连数据块路径最短速度最快。下面那条3 个间接指针一层层往下指每一层都是装满指针的索引块最终才指到数据块。为什么要搞这么复杂一句话小文件走捷径大文件绕远路但能装得下。大部分文件都不大12 个直接块就够用查找一次到位。偶尔遇到超大文件再动用间接指针牺牲一点速度换来巨大容量。能管多大以 4KB 块为例一个索引块能装1024个指针4KB ÷ 4字节 1024于是// 直接块12个指针一次到位直接块12×4KB48KB// 一级间接1个索引块 → 1024个数据块一级间接1024×4KB4MB// 二级间接套两层索引二级间接1024×1024×4KB4GB// 三级间接套三层索引三级间接1024×1024×1024×4KB4TB汇总如下指针类型原理最大容量直接块 (12个)直连数据块48 KB一级间接1层索引表4 MB二级间接2层索引表4 GB三级间接3层索引表4 TB六、Ext 文件系统架构块组模型6.1 整体布局Ext2/3/4 将分区划分为多个等大小的块组Block GroupBoot Block固定 1KB存放启动代码和分区表文件系统不能修改。6.2 单个块组的 6 大组成每个块组的内部结构如下超级块Super Block存放全局信息块/inode 总量、大小、空闲量、挂载时间等。每个块组都有备份第一个必须后续可选防止局部损坏。GDT块组描述符表记录本组内位图、inode 表、数据块的起始位置及空闲计数。同样有备份。块位图Block Bitmap用位标记数据块占用情况0空闲1占用。inode 位图Inode Bitmap标记 inode 占用情况。inode 表Inode Table该组所有 inode 的数组。数据块Data Blocks存放文件真实内容或目录内容。⚠️注意inode 编号和 block 号均以分区为单位不可跨分区6.3 超级块代码片段structext2_super_block{__le32 s_inodes_count;__le32 s_blocks_count;__le32 s_free_blocks_count;__le32 s_free_inodes_count;__le16 s_magic;// 魔数 0xEF53// ... 更多字段};6.4 创建文件全过程以touch abc为例当前目录下执行touch abcinode 号假设为 263466存储属性找到一个空闲 inode263466写入文件类型、权限、时间戳等。存储数据空文件暂不分配数据块若有内容找到空闲块写入。记录分配更新 inode 位图和块位图在 inode 中记录块号列表。添加目录项在当前目录的数据块中添加(263466, abc)完成文件名与 inode 的绑定。七、目录的本质90% 的人理解错了7.1 目录也是文件目录文件的数据块中存放的是“文件名 → inode号”的映射表。磁盘上没有“目录”这个特殊概念只有文件属性 文件内容。7.2 C 代码验证// readdir_demo.c#includestdio.h#includestring.h#includestdlib.h#includedirent.hintmain(intargc,char*argv[]){if(argc!2){fprintf(stderr,Usage: %s directory\n,argv[0]);exit(EXIT_FAILURE);}DIR*diropendir(argv[1]);if(!dir){perror(opendir);exit(EXIT_FAILURE);}structdirent*entry;while((entryreaddir(dir))!NULL){if(strcmp(entry-d_name,.)0||strcmp(entry-d_name,..)0)continue;printf(名称: %-20s inode号: %lu\n,entry-d_name,entry-d_ino);}closedir(dir);return0;}gcc readdir_demo.c-oreaddir_demo ./readdir_demo /ls-li/# 对比 inode 号完全一致7.3 路径解析全过程以访问/home/user/test.c为例从根目录/开始inode 号固定通常为2开机即知。读根目录的数据块找到home对应的 inode。跳转至home的 inode读其数据块找到user的 inode。进入user目录找到test.c的 inode打开文件。任何路径的解析都要从根目录逐级向下路径的第一斜杠即根目录。7.4 路径缓存dentry每次都从根目录解析太慢。内核为每个打开的文件在内存中维护一个dentry结构形成树形路径缓存。dentry 同时挂载在LRU 链表用于淘汰和哈希表用于快速查找中极大加速重复访问。structdentry{atomic_td_count;unsignedintd_flags;/* protected by d_lock */spinlock_td_lock;/* per dentry lock */structinode*d_inode;/* Where the name belongs to - NULL is * negative *//* * The next three fields are touched by __d_lookup. Place them here * so they all fit in a cache line. */structhlist_noded_hash;/* lookup hash list */structdentry*d_parent;/* parent directory */structqstrd_name;structlist_headd_lru;/* LRU list *//* * d_child and d_rcu can share memory */union{structlist_headd_child;/* child of parent list */structrcu_headd_rcu;}d_u;structlist_headd_subdirs;/* our children */structlist_headd_alias;/* inode alias list */unsignedlongd_time;/* used by d_revalidate */structdentry_operations*d_op;structsuper_block*d_sb;/* The root of the dentry tree */void*d_fsdata;/* fs-specific data */#ifdefCONFIG_PROFILINGstructdcookie_struct*d_cookie;/* cookie, if any */#endifintd_mounted;unsignedchard_iname[DNAME_INLINE_LEN_MIN];};/* small names *八、挂载Mount多分区统一成一棵树8.1 挂载的本质分区格式化后不能直接使用必须挂载到现有目录树中的某个空目录挂载点上。挂载后访问该目录即访问该分区。8.2 动手实验用文件模拟分区挂载# 1. 创建 5MB 空镜像ddif/dev/zeroof./disk.imgbs1Mcount5# 2. 格式化为 Ext4mkfs.ext4 disk.img# 3. 创建挂载点sudomkdir-p/mnt/mydisk# 4. 挂载sudomount-text4 ./disk.img /mnt/mydiskdf-h|grepmydisk# 5. 卸载sudoumount/mnt/mydisk8.3 Loop 设备/dev/loop0~/dev/loop7是回环设备允许把普通文件当作块设备使用实现像物理磁盘一样挂载 ISO、镜像文件。ls-l/dev/loop*九、文件系统核心原理全景总结经过前面从物理磁盘到软硬链接的层层剖析我们现在可以站在全局高度把 Ext 文件系统的核心设计串成一条完整的逻辑链。9.1 一张表看清核心概念演变层次核心概念本质关系物理层扇区Sector磁盘最小物理读写单元512B物理基础寻址层LBA扇区的一维线性地址CHS→LBA固件自动转换逻辑层块Block文件系统最小读写单元常见4KB块 8个扇区格式化时确定管理层块组Block Group分区内多个等大的管理单元每个块组独立管理所属 inode 和数据块元数据层inode文件的“身份证”存属性和数据块指针一个文件对应一个 inode大小固定数据层Data Block存放文件内容或目录内容inode 中i_block[15]指向这些块目录层目录文件存“文件名→inode号”映射目录也是文件数据块存映射表缓存层dentry内存中的路径缓存结构树形组织挂 LRUHash加速路径解析虚拟层挂载Mount将分区根目录嫁接到目录树节点实现多分区统一目录树9.2 从磁盘到文件的全链路流程第一步物理寻址磁盘物理上由盘片、磁头、柱面、扇区组成。磁头共进退所有盘面同一半径的磁道构成一个柱面。一个磁道展开为一维扇区数组→一个柱面展开为二维数组行磁头/盘面列扇区→整个磁盘是多个柱面叠加的三维数组。最终 OS 将所有扇区抽象为一维 LBA 线性地址数组磁盘固件负责 LBA ↔ CHS 自动转换。第二步块与分区OS 按“块”8个扇区4KB为最小单位进行读写。磁盘被划分为多个分区每个分区内部再划分为多个等大的块组。第三步文件系统格式化格式化 向分区写入管理信息超级块、GDT、块位图、inode位图、inode表等。这些管理信息定义了如何在该分区内分配 inode 和数据块、如何找到文件。第四步文件创建与存储文件 元属性inode数据内容Data Block两者分离存储。inode 固定大小128/256B存储权限、时间戳、硬链接计数、数据块指针数组i_block[15]。文件名不在 inode 中而是存储在父目录的数据块里形成“文件名→inode号”映射。第五步文件定位路径解析访问/home/user/test.txt时从根/开始inode固定逐级读目录数据块查找下一级 inode直到目标文件。内核用dentry 树形结构在内存中缓存已解析路径挂入 LRU淘汰冷数据和 Hash 表快速查找。第六步分区整合挂载分区格式化后不能直接使用必须mount到某个目录挂载点。挂载后该目录即成为访问该分区的入口多个分区由此统一为一棵目录树。9.3 关键设计思想的总结① 分层抽象物理层扇区/磁道/柱面 → 寻址层LBA → 逻辑层块 → 管理层块组 → 元数据层inode → 缓存层dentry → 虚拟文件系统层VFS。每一层屏蔽下层复杂性上层只需关心本层接口。② 分离原则属性与内容分离inode 存属性Data Block 存内容。文件名与 inode 分离文件名在目录中inode 独立编号。这种分离让硬链接多个文件名指向同一 inode成为可能也带来灵活性。③ 局部性原理块组设计让 inode 和对应的数据块尽量靠近减少磁头寻道。位图集中管理空闲资源分配和回收效率高。④ 索引灵活性i_block[15]中 12 直接块 1/2/3 级间接块的设计同时兼顾小文件的高效与大文件的容量。⑤ 缓存加速dentry 路径缓存、inode 缓存、页缓存等多级缓存机制大幅减少磁盘 I/O。Ext 文件系统完整架构示意图十、硬链接与软链接彻底弄懂10.1 硬链接Hard Linktouchoriginal.txtlnoriginal.txt hardlink.txtls-lioriginal.txt hardlink.txt输出示例263466 -rw-r--r-- 2 user user 0 Jan 10 12:00 hardlink.txt 263466 -rw-r--r-- 2 user user 0 Jan 10 12:00 original.txt多个文件名指向同一个 inode链接计数为 2。不占用新 inode只是目录中多了一条映射记录。不能跨分区、不能给目录创建.和..由系统维护。10.2 软链接Symbolic Linkln-soriginal.txt softlink.txtls-lioriginal.txt softlink.txt263466 -rw-r--r-- 1 user user 0 ... original.txt 261680 lrwxrwxrwx 1 user user 12 ... softlink.txt - original.txt相当于 Windows 快捷方式独立文件有自己的 inode 和数据块。数据块中存储的是目标文件路径字符串。可跨分区、可链接目录。源文件删除后软链接变为“悬挂”状态失效。10.3 对比总结表特性硬链接软链接inode 号相同不同跨文件系统不可以可以链接目录不可以可以源文件删除后仍然可用计数0失效悬挂本质多个目录项指向同一 inode存放目标路径的独立文件10.4 删除文件的真正逻辑rm file的执行流程在其父目录的数据块中删除file → inode号的记录。对应的 inode 中i_links_count减 1。若链接数降为0且无进程占用则释放 inode 及所有数据块在位图中标记为空闲。10.5 软硬链接的典型用途硬链接文件备份、.和..的实现。软链接快捷方式、库版本管理如/usr/bin/python - python3。十一、高频易错点与面试常考扇区 ≠ 块扇区 512B物理块常见 4KB逻辑。文件名不在 inode 里文件名存在目录的数据块中。硬链接不能跨分区inode 编号只在分区内唯一。软链接是独立文件有自己的 inode存的是路径字符串。访问文件需要路径上所有目录的 x 权限。“磁盘满”可能有两种数据块满df -h查看。inode 耗尽df -i查看每个文件消耗一个 inode。mv在同一分区内极快仅修改目录项不动实际数据。跨分区则是复制删除。十二、常用命令速查表命令功能fdisk -l查看磁盘与分区stat file查看文件完整 inode 信息ls -i / ls -li查看 inode 号及详细信息mkfs.ext4 /dev/sda1格式化分区mount /dev/sda1 /mnt挂载分区umount /mnt卸载分区ln file link创建硬链接ln -s file link创建软链接df -h查看磁盘空间使用df -i查看 inode 使用情况dumpe2fs /dev/sda1查看文件系统详细信息十三、总结从扇区、磁道、柱面的三维物理结构到拉直成一维 LBA 数组的逻辑抽象从 Ext 的块组布局到 inode 的多级索引从目录本质的颠覆认知到 dentry 路径缓存的加速机制最后落地的挂载、软硬链接的实际使用——这篇博客带你从底层硬件到上层命令全面吃透 Linux Ext 文件系统。掌握这些你将真正理解“磁盘满”和“inode 耗尽”的区别能灵活运用硬链接、软链接管理文件清楚mount、格式化、分区的底层到底做了什么在面试中对文件系统问题对答如流如果本文对你有帮助欢迎点赞、收藏、评论你的支持是我持续输出的动力