云计算百科
云计算领域专业知识百科平台

【Linux系统】【文件系统底层揭秘:软硬链接与inode的真相】流食般投喂

Linux文件系统底层原理全解析:从磁盘抽象到软硬链接

本章节的核心目标只有一个:把“文件没有被打开时是怎么躺在磁盘上的”这件事彻底讲清楚。当你真正理解 inode、块组、路径缓存和链接计数后,再看 ls、find、ln,甚至误删恢复,都会有完全不同的认知。


一、从物理磁盘到逻辑抽象:一切源于“解耦”

1.1 磁盘的物理世界

在拆开文件系统这个“软件层”之前,老师先用机械磁盘的物理结构帮我们建立了直觉:

  • 盘片、磁头、传动臂:盘片高速旋转,磁头来回摆动。
  • 扇区(Sector):磁盘的基本存储单位,512 字节。物理寻址使用 CHS(柱面 C、磁头 H、扇区 S)。
  • 磁性原理:盘片表面是无数微小的磁性区域,本质上可以理解为“512 个小磁铁”构成一个扇区,用南北极表示 0 和 1。

1.2 逻辑抽象:三维 → 一维

操作系统并不想在代码里维护一套复杂的三维坐标。于是磁盘被逻辑抽象成一维数组:

  • 所有相同半径的磁道连起来,形成一个二维平面;所有柱面再堆叠,变成三维结构。
  • 最终在操作系统看来,整个磁盘就是一个大的一维数组,每个元素是一个扇区。
  • 从此寻址不再用 CHS,而改用 LBA(逻辑块地址)——一个单纯的数组下标。
  • 磁盘内部会自动完成 LBA 与 CHS 的相互转换,对上层完全透明。

1.3 为什么是 4KB 的“块(Block)”?

这是一个经常被忽略、但极其关键的设计。老师强调了两个原因:

  • 效率:机械磁盘随机 IO 很慢。如果操作系统每次只读写 512 字节,磁头需要极度频繁地寻道。将 8 个扇区(512B × 8)拼成一个 4KB 的 Block,可以大幅减少寻址次数,提高连续读写效率。
  • 软硬件解耦:如果操作系统直接按 512 字节与磁盘交互,那么一旦磁盘技术迭代(比如出现新物理扇区大小),操作系统就得跟着改。引入 4KB 的 Block 作为标准交互单位后,无论底层磁盘怎么变,文件系统层都无需改动。
  • 💡 零碎知识点:4KB 这个数字不是拍脑袋定的,而是大量实验得出的平衡值。后续讲内存管理时还会提到,内存也是以 4KB(页)为单位管理,磁盘 Block 与此对应,做好了“随时载入内存”的准备。


    二、分区、格式化与 Ext2 块组结构

    2.1 分区:分而治之

    一个大容量磁盘(比如 800GB)无法直接管理。操作系统把它切成多个分区(比如 300GB、200GB)。每个分区可以独立格式化,使用不同的文件系统(如 Ext2、Ext3、Ext4、XFS(一种高性能的日志式文件系统))。

    分区的本质只是在磁盘开头记录每个分区的 Start 和 End LBA,类似于当年虚拟地址空间的划分思想。

    2.2 格式化的真相:不是清空,而是“画格子”

    很多人以为格式化就是擦除数据。实际上格式化根本不碰你的数据内容,它只是向分区写入文件系统的管理信息,包括:

    • 超级块(Super Block)
    • 组描述符表(GDT)
    • Inode 位图 / Block 位图(清零,标记全部为空闲)
    • 然后按规则划分出 块组(Block Group)

    💡 零碎知识点:删除文件之所以“秒删”,就是因为系统只需要把位图对应比特位清零,数据块本身连碰都不碰。这也解释了为什么误删后只要不再写入新文件,数据是可以恢复的——那些位图只是被标记为“可用”,原数据还在那里。

    2.3 一个块组的解剖图(Ext2)

    老师反复强调:文件系统以分区为载体(文件系统依赖于分区作为其物理载体,分区提供了一块独立的存储空间,文件系统则在此之上构建逻辑结构以管理数据),研究透分区内一个块组,就研究透了整个分区。一个块组包含以下核心结构:

    结构作用
    Super Block 描述整个文件系统的宏观信息:inode 总数、block 总数、块大小、最近写入时间等。注意:为了防单点故障,超级块会在多个块组中做备份,内容完全相同。一旦某份损坏,可以用其他的恢复。
    GDT(Group Descriptor Table) 描述当前块组的详细信息:inode 表从哪里开始、data block 从哪里开始、空闲 inode 数、空闲数据块数等。
    Block Bitmap 记录数据块占用情况。1 个比特位 对应 1 个 4KB 数据块。
    Inode Bitmap 记录 inode 节点占用情况。
    Inode Table 存放文件属性(权限、UID/GID、大小、时间戳、数据块指针等)。文件名不在这里!
    Data Blocks 存放文件实际内容,以 4KB 为单位。

    在这里插入图片描述

    关于 Inode 的几个零碎细节:
    • 固定大小:在 Ext2/3/4 中,一个 inode 通常是 128 字节(不排除未来新文件系统会变化)。
    • 局部性原理:Inode Table 也是以 4KB 为单位存储的。一个块能装下 4096 / 128 = 32 个 inode。文件系统一次性把这 32 个 inode 读进内存,因为短时间内创建的文件往往具有关联性,后续访问很可能命中缓存。
    • 全局唯一:inode 编号和 data block 编号是在分区内全局唯一的,可以跨块组,但绝对不能跨分区。不同分区可以各自从 0 开始编号。(分区:国家;块组:省市;国家对国家,管理方式是不通的,国家内对省市的管理是相通的)
    • 定位公式:给定一个 inode 编号,通过除法 + 取模即可算出它在哪个块组、在块组内的哪个位置。数据块同理。

    三、目录的本质与路径解析:文件名去哪了?

    3.1 目录也是“普通文件”

    这是本章节最具颠覆性的认知之一:

    在磁盘层面,根本没有“目录”这个概念。磁盘上只有 inode 和 data block 两种东西。

    目录与普通文件的存储方式完全一样:

    • 目录有自己的 inode(里面存权限、类型等属性)。
    • 目录有自己的 data block。

    唯一不同的是内容:目录的数据块里存储的不是普通文本或二进制,而是一张**“文件名 → inode 编号”的映射表**。

    这就是为什么:

    • 文件名不保存在文件的 inode 中(因为文件名要用来当索引)!文件名保存在它所属目录的数据块里。
    • 在同一个目录下,文件名不能重复(映射表的 key 必须唯一)(这也就是为什么你在相同目录下,新建文件,系统会给出“新建文件”、“新建文件1”、“新建文件2”…)。

    3.2 找文件必须从根目录“逐级解析”

    既然文件名散落在各级目录里,那访问文件就必须从根目录开始“层层揭开”:

  • 系统默认在开机时就固定打开了根目录。
  • 你提供文件名(如 code.c),进程 PCB 提供当前工作目录 cwd(Current Working Directory)。
  • 两者拼接成完整路径(如 /home/whb/lesson21/code.c)。
  • 操作系统从根目录开始,读取其 data block,找到 home 的 inode;再打开 home,找到 whb 的 inode……直到最终找到 code.c 的 inode。
  • 下面我们把第 4 步拆开,用 /home/whb/lesson21/code.c 这个路径,一步步看操作系统到底做了什么:

  • 打开根目录 /:系统在开机时就已经把根目录的 inode 常驻内存(根目录 inode 编号固定,如 Ext2 中通常是 2)。拿到根目录 inode 后,读取它的 data block,得到一张映射表,里面记录了 home 这个文件名对应的 inode 编号(比如 128)。
  • 进入 home:系统拿着 inode 编号 128,去 Inode Table 里读出 home 目录的 inode(里面存权限、大小等属性),再读取 home 的 data block,得到第二张映射表,找到 whb 对应的 inode 编号(比如 256)。
  • 进入 whb:同理,读出 whb 的 inode,读取其 data block,找到 lesson21 对应的 inode 编号(比如 384)。
  • 进入 lesson21:继续读出 lesson21 的 inode,读取其 data block,找到 code.c 对应的 inode 编号(比如 512)。
  • 命中目标:最后读出 code.c 的 inode,里面存着它的权限、大小、时间戳,以及指向数据块的指针数组。顺着指针就能读到 code.c 的实际内容。
  • 整个过程可以用下面这张图直观表示:

    #mermaid-svg-BArhiz9fJ538vBc1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BArhiz9fJ538vBc1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BArhiz9fJ538vBc1 .error-icon{fill:#552222;}#mermaid-svg-BArhiz9fJ538vBc1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BArhiz9fJ538vBc1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BArhiz9fJ538vBc1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BArhiz9fJ538vBc1 .marker.cross{stroke:#333333;}#mermaid-svg-BArhiz9fJ538vBc1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BArhiz9fJ538vBc1 p{margin:0;}#mermaid-svg-BArhiz9fJ538vBc1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-BArhiz9fJ538vBc1 .cluster-label text{fill:#333;}#mermaid-svg-BArhiz9fJ538vBc1 .cluster-label span{color:#333;}#mermaid-svg-BArhiz9fJ538vBc1 .cluster-label span p{background-color:transparent;}#mermaid-svg-BArhiz9fJ538vBc1 .label text,#mermaid-svg-BArhiz9fJ538vBc1 span{fill:#333;color:#333;}#mermaid-svg-BArhiz9fJ538vBc1 .node rect,#mermaid-svg-BArhiz9fJ538vBc1 .node circle,#mermaid-svg-BArhiz9fJ538vBc1 .node ellipse,#mermaid-svg-BArhiz9fJ538vBc1 .node polygon,#mermaid-svg-BArhiz9fJ538vBc1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BArhiz9fJ538vBc1 .rough-node .label text,#mermaid-svg-BArhiz9fJ538vBc1 .node .label text,#mermaid-svg-BArhiz9fJ538vBc1 .image-shape .label,#mermaid-svg-BArhiz9fJ538vBc1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BArhiz9fJ538vBc1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BArhiz9fJ538vBc1 .rough-node .label,#mermaid-svg-BArhiz9fJ538vBc1 .node .label,#mermaid-svg-BArhiz9fJ538vBc1 .image-shape .label,#mermaid-svg-BArhiz9fJ538vBc1 .icon-shape .label{text-align:center;}#mermaid-svg-BArhiz9fJ538vBc1 .node.clickable{cursor:pointer;}#mermaid-svg-BArhiz9fJ538vBc1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BArhiz9fJ538vBc1 .arrowheadPath{fill:#333333;}#mermaid-svg-BArhiz9fJ538vBc1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BArhiz9fJ538vBc1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BArhiz9fJ538vBc1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BArhiz9fJ538vBc1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BArhiz9fJ538vBc1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BArhiz9fJ538vBc1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BArhiz9fJ538vBc1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BArhiz9fJ538vBc1 .cluster text{fill:#333;}#mermaid-svg-BArhiz9fJ538vBc1 .cluster span{color:#333;}#mermaid-svg-BArhiz9fJ538vBc1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BArhiz9fJ538vBc1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BArhiz9fJ538vBc1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BArhiz9fJ538vBc1 .icon-shape,#mermaid-svg-BArhiz9fJ538vBc1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BArhiz9fJ538vBc1 .icon-shape p,#mermaid-svg-BArhiz9fJ538vBc1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BArhiz9fJ538vBc1 .icon-shape .label rect,#mermaid-svg-BArhiz9fJ538vBc1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BArhiz9fJ538vBc1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BArhiz9fJ538vBc1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BArhiz9fJ538vBc1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    根目录 / 的 inode(编号 2)

    读取 / 的 data block

    映射表:home → inode 128

    读取 home 的 inode(128)

    读取 home 的 data block

    映射表:whb → inode 256

    读取 whb 的 inode(256)

    读取 whb 的 data block

    映射表:lesson21 → inode 384

    读取 lesson21 的 inode(384)

    读取 lesson21 的 data block

    映射表:code.c → inode 512

    读取 code.c 的 inode(512)

    读取 code.c 的 data block,得到文件内容

    💡 零碎知识点:ls 命令打印的每一行,其实就是把目录 data block 里的文件名,与该文件 inode 里的属性(权限、大小、时间)拼接后展示给你。用 ls -li 就能看到每个文件对应的 inode 编号

    💡 零碎知识点:每一步“读目录的 data block”都是一次磁盘 IO(除非命中了 Dentry 缓存)。所以路径越深,逐级解析的磁盘 IO 就越多——这正是后面第四部分要讲的 Dentry 缓存要解决的问题。

    3.3 分区定位:挂载与路径前缀

    路径解析还解决了一个关键问题:如何知道文件在哪个分区?

    • 分区必须**挂载(mount)**到某个目录下才能被访问。
    • 挂载是操作系统中将存储设备(如硬盘、U盘、光盘等)或文件系统与目录树连接的过程。通过挂载,系统可以访问存储设备中的数据。
    • 系统通过路径前缀匹配挂载点(当执行挂载操作时,指定的目录即为挂载点) 来确定文件属于哪个分区,遵循就近原则。

    老师现场做了实验:

    # 1. 用 dd 伪造一个 5MB 的“磁盘镜像”
    dd if=/dev/zero of=deepseek.img bs=1M count=5

    # 2. 格式化为 ext4 文件系统(本质是写入 super block、GDT、位图)
    mkfs.ext4 deepseek.img

    # 3. 挂载到某个目录(需要 root)
    mount -t ext4 -o loop deepseek.img ./mnt

    # 4. 此时进入 ./mnt 就相当于进入了这个分区
    touch ./mnt/test.txt

    # 5. 查看挂载状态
    df -h

    # 6. 卸载
    umount ./mnt

    当你以后访问 /home/whb/lesson21/code.c 时,系统看到路径前缀匹配了某个挂载点,就能确定你要操作的是哪个分区,然后在该分区内用 inode 编号定位。(该目录与该分区绑定,在目录层面,访问到这个目录,相当于进入你这个分区,可以开始逻辑管理了)


    四、Dentry 缓存:内核里的“多叉树”

    如果每次访问文件都要从根目录开始做磁盘 IO,效率太低了。Linux 的解决方案是 Dentry(目录项)缓存。

    4.1 内存里的路径多叉树

    内核在内存中维护了一棵动态增长的 struct dentry 多叉树:

    • 根节点是根目录。
    • 中间节点是目录。
    • 叶子节点是普通文件或空目录。
    • 每个 dentry 结点代表文件系统中一个路径节点,例如目录或文件。

    当你第一次访问某个路径时,内核从磁盘逐级加载目录节点,在内存中构建这棵树的对应分支。下次再访问相同路径时,直接在内存树中“命中”,无需磁盘 IO。

    4.2 实验:find 的“第一次慢,第二次快”

    老师课上现场演示了:

    find / -name "code.c"
    # 第一次执行:明显卡顿,因为路径树不完善

    find / -name "code.c"
    # 第二次执行:秒出结果,因为目录结构已缓存在内存

    4.3 struct dentry 的三重身份

    struct dentry 在内存中非常精妙,它同时属于三个数据结构:

  • 多叉树:维护父子目录关系,提供路径查找。
  • LRU 链表:当内存不足时,操作系统淘汰最近最少使用的节点。
  • 哈希表(d_hash list):加速同名节点查找。
  • 💡 概念:LRU 是 Least Recently Used 的缩写,中文意为“最近最少使用”。它是一种常用的缓存淘汰策略,用于在缓存空间有限的情况下决定哪些数据应该被保留,哪些应该被移除。
    原理:当缓存达到容量上限时,LRU 策略会优先淘汰最长时间未被访问的数据。也就是说,如果某个数据项在最近一段时间内没有被读取或写入,它就更有可能被移除。

    💡 零碎知识点:老师提示,Linux 中“目录树”的概念本质上是内存级的。磁盘上并没有一棵树,只有一堆 inode 和 data block。


    五、从 inode 到数据块:超大文件如何映射?

    找到 inode 后,如何拿到文件内容?inode 里有一个数据块指针数组(早期内核版本 Ext2 里是 15 个)。

    5.1 直接索引与间接索引

    • 前 12 个指针:直接指向 data block,适合小文件。
    • 第 13 个指针:一级间接索引。它指向的块不存文件内容,而是存下一级 data block 的地址(4KB 块可以存 1024 个 4 字节地址)。凭空多出 1024 个数据块。
    • 第 14 个指针:二级间接索引。多一层跳转,多出 1024 × 1024 个块。
    • 第 15 个指针:三级间接索引。再多一层,容量达到 1024 × 1024 × 1024 × 4KB,文件上限极其庞大。在这里插入图片描述

    5.2 跨块组存储

    如果文件特别大,超过了本块组的 data block 数量怎么办?

    • 没关系,数据块可以跨块组存储。(都是中国省市,可以跨省运作)
    • 只要 inode 编号和 block 编号在分区内全局唯一,系统就能通过全局编号找到它们。

    六、软硬链接:看似相似,本质迥异

    6.1 软链接(Symbolic Link):独立的“快捷方式”

    创建命令:ln -s source(源文件或目录) target(目标路径)

    • 独立文件:拥有独立的 inode。
    • 内容:data block 里保存的是目标文件的路径字符串。
    • 跨分区:可以跨分区链接。
    • 特性:删除源文件后,软链接变成“悬空(Dangling)”状态。

    老师用 Windows 快捷方式做了类比:软链接就像桌面的 Chrome 快捷方式,双击时系统去读它指向的目标路径,再去开真正的程序。

    6.2 硬链接(Hard Link):新增一条映射记录

    创建命令:ln source(源文件) target(目标文件)

    • 非独立文件:没有独立的 inode,而是目录里新增了一条“文件名 → 目标 inode”的映射。
    • 共享 inode:硬链接与源文件共用同一个 inode 编号。
    • 不可跨分区:因为 inode 不能跨分区。
    • 引用计数:inode 里有一个 link count(链接计数)。新建一个硬链接,计数 +1;删除一个,计数 -1。只有当计数归零时,文件数据才会真正被释放。
    目录的硬链接:. 与 .. 的奥秘

    这是课上最巧妙的知识点之一:

    • 每个目录下默认都有 .(当前目录)和 ..(上级目录)。
    • . 是当前目录自身的硬链接,所以新建一个空目录,它的链接数是 2(一个是自己的目录名映射,一个是 .)。
    • .. 是父目录的硬链接。你在父目录下每新建一个子目录,父目录的链接数就会 +1。

    💡 应用:你可以通过 ls -ld 目录名 看到链接数,链接数 – 2 = 该目录下真正的子目录数量(不包括 . 和 ..)。老师举例:根目录的链接数是 18,说明根目录下有 16 个一级子目录。

    硬链接的实际用途:秒级备份

    老师举了一个“防误删”的场景:

    ln code.c code.c.bak
    # 现在 code.c 和 code.c.bak 指向同一个 inode
    # 别人执行 rm code.c,你的 code.c.bak 依然能访问内容
    # 因为 inode 的 link count 还没归零

    不需要复制文件内容,不到一秒完成备份,这就是硬链接的工程价值。


    七、课堂代码实证:不只是理论

    7.1 观察 inode 与目录映射

    ls -li

    输出示例(课上数据):

    1321114 -rw-rw-r– 1 whb whb 0 Jan 15 15:01 code.c
    1321115 drwxrwxr-x 2 whb whb 4096 Jan 15 15:02 dir

    左边第一列就是 inode 编号。code.c 的 inode 是 1321114,dir 的是 1321115。

    7.2 用代码直接读取目录内容

    老师准备了一段 readdir.c,核心逻辑是用 opendir() 打开目录,再用 readdir() 遍历:

    // 读取目录时,实际上读的是该目录 data block 里的
    // "文件名 – inode 编号" 映射表
    while ((entry = readdir(dir)) != NULL) {
    printf("文件名: %s, inode: %lu\\n", entry->d_name, entry->d_ino);
    }

    运行后你会看到目录里每个条目及其 inode,直观验证了目录的存储本质。

    opendir:opendir 是一个在编程中用于打开目录的函数,常见于 C 语言和 PHP 等语言中。它主要用于访问文件系统中的目录内容,以便读取其中的文件或子目录。
    readdir:readdir 是一个在操作系统中用于读取目录内容的函数,常见于 C 语言标准库以及类 Unix 系统(如 Linux、macOS)的文件系统编程中。它主要用于遍历目录中的文件和子目录。

    7.3 软硬链接对比实验

    ln -s code.c code-soft # 软链接
    ln code.c code-hard # 硬链接
    ls -li

    观察结果:

    • code-soft 会有一个全新的 inode 编号(如 1321116)。
    • code-hard 的 inode 编号与 code.c完全相同(如都是 1321114)。
    • code.c 的硬链接数会从 1 变成 2。

    总结:文件系统的设计哲学

    整节课下来,老师始终在强调一个核心思想——“先描述,再组织”:

  • 描述:文件 = 内容(Data Blocks)+ 属性(Inode)。二者分开存储,通过 inode 编号全局管理。
  • 组织:目录不特殊,只是存映射表的文件;路径不神秘,只是从根目录开始的逐级查找;分区不复杂,只是挂载到目录树上的一个分支。
  • 加速:Dentry 缓存、块(Block)、间接索引……一切都是为了在空间、速度与稳定性之间取得平衡。
  • 当你下一次在 Linux 下执行 ls、find、rm、ln 时,你的脑海中浮现的不再只是一行行命令,而是一整幅清晰的画卷:磁头在 LBA 上寻址、块组里的位图在翻转、dentry 树在内存中生长、inode 的链接计数在悄悄变化。


    希望这篇基于课堂的博客,能帮你把 Linux 文件系统的底层脉络彻底打通。如果有任何疑问,欢迎在评论区继续探讨!

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【Linux系统】【文件系统底层揭秘:软硬链接与inode的真相】流食般投喂
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!