Skip to content

Instantly share code, notes, and snippets.

@KuRRe8
Created October 31, 2025 03:31
Show Gist options
  • Select an option

  • Save KuRRe8/61fa8a439088276c3f434b74bd3aa5c2 to your computer and use it in GitHub Desktop.

Select an option

Save KuRRe8/61fa8a439088276c3f434b74bd3aa5c2 to your computer and use it in GitHub Desktop.

硬盘和文件系统

硬盘访问逻辑与基本概念

下面概括机械硬盘/SSD在操作系统视角下的寻址与I/O基本面,便于后续讨论文件系统、分区和性能优化。

快速要点

统一寻址:现代系统用 LBA(Logical Block Addressing)按块编号访问,不再用CHS。 基本单元:逻辑扇区通常为 512B(或4K);物理扇区常见为 4K(Advanced Format)。 对齐很关键:分区与文件系统数据结构应按 4K(甚至1MB)对齐,避免读-改-写(RMW)罚时。 顺序优于随机:磁盘顺序访问远快于随机;队列与调度可缓解随机I/O开销。 持久性与一致性:缓存写回需配合刷新(flush/fsync),以确保掉电后数据落盘。 健康与重映射:坏扇区会被设备透明重映射,但可能引入不可预期的长延迟。

  1. 逻辑扇区与物理扇区 物理扇区(Physical Sector) 磁盘或SSD内部实际读写的最小物理块,现代硬盘多为 4KiB(4Kn)。 逻辑扇区(Logical Sector) 操作系统看到的最小地址单元,常见为 512B 或 4KiB。 512e(512-byte emulation):设备物理4K,但对外“模拟”512B逻辑扇区。未对齐到4K的写入会触发读-改-写(RMW)流程,增加延迟与写放大。 原子性 通常以“逻辑扇区大小”为原子写单元(崩溃不应撕裂跨扇区的部分,仍需文件系统/日志机制保障更高层一致性)。
  2. 寻址方式:CHS 与 LBA CHS(Cylinder-Head-Sector) 早期按柱面/磁头/扇区定位,已不适用于现代设备容量与内部布局。 LBA(Logical Block Addressing) 用连续编号的“块号”表示扇区位置,LBA从0开始递增。 操作系统/控制器将 LBA 映射到设备内部的实际位置与闪存/磁道布局。
  3. 扇区大小与对齐 扇区大小 常见组合:逻辑512B + 物理4K(512e),或逻辑/物理同为4K(4Kn)。 对齐原则 分区起始 LBA 应对齐到 4K,常用“1MB 对齐”(起始偏移 1,048,576 字节),可同时满足4K与多数RAID条带对齐需求。 文件系统元数据、数据区、日志/日志组建议保持4K或更大粒度对齐,降低RMW与写放大。 检查与实践 Windows 默认新建分区采用 1MB 对齐;跨平台迁移或历史分区需复核。
  4. I/O 路径与调度(简版) 路径概览 应用 → 文件系统(缓存/日志) → 块层/队列 → 主机控制器/驱动 → 设备固件 → 介质。 调度与队列 HDD:调度倾向合并/重排请求以减少寻道(顺序化有益)。 SSD/NVMe:多队列并行(提交/完成队列),性能与队列深度、并发度相关;仍建议合并小IO以降低开销。
  5. 顺序访问与随机访问 顺序访问 HDD:显著减少寻道,吞吐与延迟最优。 SSD:也可减少控制器/总线与元数据开销,提升写入放大控制。 随机访问 HDD:寻道主导延迟,性能下降明显。 SSD:控制器与并行度可缓解,但小随机写仍可能触发擦写与GC。
  6. 分区表与起始扇区(MBR/GPT) MBR 传统分区表,位于 LBA0;最大2TB(32位LBA)限制;最多4个主分区(或扩展分区)。 GPT 现代分区表,主头位于 LBA1,支持更大地址空间与更多分区;包含备份头。 兼容性:有 Protective MBR 以避免旧工具误操作。 起始扇区与对齐 GPT下常见第一个分区从 1MB 边界开始,对齐4K且预留空间给对齐和元数据。
  7. 缓存、刷写与顺序保证 主机页缓存与设备缓存 写入先进入内存页缓存与/或设备缓存(write-back),并非立刻落盘。 刷写与持久化 应用层应在关键路径调用刷新原语确保数据持久(例如:Linux fsync/fdatasync;Windows FlushFileBuffers)。 写屏障/强制单元访问(FUA)用于确保顺序与落盘;设备与驱动需正确实现。 掉电保护 企业级设备可能具备掉电保护电容,写回策略更安全;消费级设备建议应用与文件系统更保守地刷写关键数据。
  8. 坏扇区与重映射 透明重映射 设备固件将坏扇区重映射到预留扇区,LBA视图不变。 性能与可观测性 重映射可能引入个别请求的极长延迟(如反复重试后成功/失败)。 通过 S.M.A.R.T. 指标(如 Reallocated Sector Count、Current Pending Sector)可观察健康趋势,必要时尽早更换。
  9. 常用术语小词典 LBA:逻辑块地址,从0起的块编号。 逻辑扇区:OS可见的最小读写单元(512B/4K)。 物理扇区:设备内部的真实最小单元(常见4K)。 512e/4Kn:先进格式;512e为物理4K、逻辑512B;4Kn为逻辑=物理4K。 RMW:读-改-写;未对齐小写入在4K物理扇区上常见的额外代价。 NCQ/NVMe 队列:命令队列机制,用于并行与重排。 fsync/FlushFileBuffers:强制数据从缓存落盘的接口。 写屏障/FUA:确保写入顺序与持久化语义的机制。 S.M.A.R.T.:设备健康监测指标集合。

1. 分区:MBR 与 GPT

1.1 MBR 概述

  • 位置与大小:磁盘第 0 扇区(通常 512B)。
  • 布局:
    • 启动代码区(Bootstrap)
    • 分区表(4 个主分区条目,每条 16B,共 64B)
    • 结束签名 0x55AA
  • 能力与限制:
    • 最多 4 个主分区;可用扩展分区容纳更多逻辑分区
    • 使用 32 位 LBA 与传统 512B 扇区时,容量上限约 2 TiB
    • 无冗余校验,元数据易受损
  • 启动路径(典型 BIOS):BIOS -> MBR -> 分区引导扇区(VBR) -> 引导加载器/内核

1.2 GPT 概述

  • 兼容头:LBA 0 放置 Protective MBR(防止旧工具误覆盖)
  • 主结构:
    • GPT Header(LBA 1)
    • 分区条目数组(紧随其后,默认 128 项、每项 128B,可扩展)
    • 备份 GPT Header 与条目数组位于磁盘末尾(冗余)
  • 特性:
    • 64 位 LBA,支持超大容量
    • CRC32 校验,元数据冗余更安全
    • 每个分区有“分区类型 GUID”和“唯一分区 GUID”
    • UEFI 原生支持,仍可与传统 MBR 共存(Protective MBR)

1.3 MBR vs GPT 简述

  • GPT 容量更大、元数据具校验与冗余、分区数量不再受 4 个主分区限制,是现代首选。
  • 旧系统或特定场景可能仍使用 MBR;新部署建议优先 GPT。

2. NTFS 文件系统

2.1 逻辑布局(简述)

  • 卷引导扇区(VBR / $Boot)
  • $MFT(主文件表)与 $MFTMirr(镜像)
  • $LogFile(日志)、$Bitmap(簇位图)、$BadClus(坏簇)、$UpCase、$Secure 等
  • 一切皆文件:这些“元文件”自身也是 NTFS 文件

2.2 簇(Cluster/Allocation Unit)

  • 定义:文件系统最小分配单位,常见为 4 KiB,也可按格式化时选项调整。
  • 用户视角:
    • Windows 格式化时可选“分配单元大小”
    • 资源管理器显示“大小”与“占用空间”
  • 文件大小 vs 占用空间:
    • 占用空间按簇对齐:哪怕只写入 1B,也至少占用 1 个簇(内部碎片)
    • 压缩文件/稀疏文件会使“占用空间”小于“大小”
    • 流(Alternate Data Streams)与元数据可能影响统计口径

2.3 小文件驻留 MFT(Resident Data)与“占用空间 0”

  • NTFS 把文件表示为一系列“属性”(Attribute),例如:
    • $STANDARD_INFORMATION、$FILE_NAME、$SECURITY_DESCRIPTOR
    • $DATA(文件内容,既可驻留也可非驻留)
  • 当 $DATA 属性为“驻留(Resident)”时,文件内容直接存放在 MFT 记录里,不分配簇。
    • 这类小文件在资源管理器中“占用空间”可能显示为 0 KB
    • 实际仍占用 MFT 记录空间(典型 1 KiB 或 4 KiB),但该空间不计入“占用空间(按簇计)”

2.4 MFT 与目录/索引

  • 每个文件/目录对应一个或多个 MFT 记录(记录中是属性列表)
  • 常见属性:
    • $DATA:驻留/非驻留数据
    • $INDEX_ROOT / $INDEX_ALLOCATION:目录索引(B+ 树样式)
    • $ATTRIBUTE_LIST:跨记录/文件拼接属性
  • $Bitmap 跟踪簇分配,$MFT 自身也会扩展并分配簇存放非驻留部分

2.5 链接类型(Windows 视角)

  • 硬链接(hard link):
    • 同一卷内为同一文件内容创建多个目录项;共享同一 MFT 条目/数据
    • 删除其中一个路径不影响其他路径,引用计数为 0 才真正释放
    • 仅对文件有效(目录硬链接仅系统内部使用)
  • 符号链接(软链接,symlink):
    • 以路径为目标,可指向文件或目录,可跨卷
    • 需要相应权限或开启“开发者模式”
    • 目标不存在时为“悬挂链接”
  • 连接点/目录联接(junction):
    • 基于重解析点的目录级跳转,多用于跨目录/卷重定位
  • 快捷方式(.lnk):
    • 用户层文件,不是文件系统级链接

2.6 直接读盘与 ACL

  • NTFS 的访问控制(ACL)在文件系统层由驱动强制。
  • 以原始方式读取设备(例如 \.\PhysicalDriveN,或以足够权限打开原始卷句柄)会绕过文件级 ACL 检查,直接获取原始扇区数据。
    • 前提:需要管理员/备份权限,可能需要锁定/脱机卷以避免并发一致性问题
    • 加密限制:
      • BitLocker 未解锁时,原始读到的是密文
      • EFS 针对文件级加密,未持有密钥时即使通过备份语义也无法解密内容
    • 备份相关 API 可在权限允许下绕过 ACL 读取文件,但仍受加密与审计约束

3. Linux 的 EXT4

3.1 逻辑布局(简述)

  • 分区内起始布局:
    • 预留区:从分区起始偏移 0–1023 字节通常为空/引导代码保留区。
    • 超级块(superblock):位于偏移 1024B(块 0 之后),记录卷级关键元数据与特性位。
    • 组描述符表(Group Descriptors):紧随其后,描述每个块组的元数据位置与统计信息。
    • 分块组(Block Group)结构:每组包含 块位图、inode 位图、inode 表、数据块,可能还有组副本元数据。
    • 日志(JBD2):通常为文件系统内的隐藏日志文件(也可外置设备)。
  • 关键特性:
    • Extents(范围分配,替代间接块),提升大文件与顺序 I/O 效率。
    • 目录 HTree 索引(基于哈希的 B-Tree 变体),提升大目录查找性能。
    • 延迟分配(delalloc)、多块分配(mballoc)、64 位块号、在线扩容。
    • 元数据校验(metadata_csum)、快速提交(fast_commit,可选)、加密(fscrypt,可选)。

3.2 块(Block/Allocation Unit)

  • 块大小:常见为 4 KiB(也支持 1/2 KiB 等,取决于创建时参数与平台)。
  • 分配粒度:以块为最小分配单位,文件内容通过“extent”映射逻辑块区间到物理块区间。
  • 碎片:
    • 内部碎片:文件大小不是块大小整数倍时最后一块未用部分。
    • 外部碎片:文件的块在磁盘上不连续;extents 与 mballoc 有助于降低。

3.3 小文件驻留 inode(inline data,可选特性)

  • 当启用 inline_data 且文件很小(数百字节量级),内容可直接存放在 inode 的 i_block/xattr 空间里,不占用单独数据块。
  • 类似“驻留数据”的效果:
    • 优点:减少小文件的块分配与寻址开销,提升小 I/O 性能。
    • 观测:逻辑大小>0,但磁盘实际占用块可能为 0(除 inode 表本身)。
  • 符号链接的“快速 symlink”也可将短目标路径直接放在 inode 中。

3.4 Inode、目录与索引

  • Inode 记录:
    • 基本属性:文件类型与权限(mode)、UID/GID、链接计数、大小。
    • 时间戳:atime/mtime/ctime 以及可选 crtime(创建/出生时间),含纳秒字段。
    • 数据位置:extents 根(i_block 中的 extent header/索引/叶子)或旧式间接块指针(已被 extents 取代)。
    • 标志位:如 immutable、append-only、noatime 等。
    • 扩展属性(xattr):存储 POSIX ACL、SELinux 标签、fscrypt 策略、fs-verity 信息、用户自定义属性等。
  • 位图与表:
    • 块位图:跟踪每组数据块的空闲/占用。
    • inode 位图:跟踪每组 inode 的空闲/占用。
    • inode 表:顺序存放该组的 inode 记录。
  • 目录与 HTree:
    • 目录以目录项(dentry)列表形式存储:文件名 → inode 号。
    • 大目录启用 HTree 索引(哈希+树形索引)加速查找;叶子仍是成批的目录项块。

3.5 链接类型

  • 硬链接(hard link):
    • 同一文件系统内为同一 inode 建立多个目录项;inode 链接计数控制释放时机。
    • 不能对目录创建普通硬链接(. 与 .. 为特殊保留)。
  • 符号链接(symlink,软链接):
    • 保存目标路径,可跨文件系统、可指向目录;短路径可 inline 存储。
  • 说明:
    • bind mount 属于 VFS/挂载层语义,并非 ext4 链接。
    • 新版内核/工具链可选提供 reflink/克隆(写时复制),需内核与 mkfs/mount 选项支持。

3.6 日志(JBD2)与数据模式

  • 日志位置:内置日志文件(历史上常为隐藏 inode),或外置独立设备/分区。
  • 事务:元数据修改被打包为事务写入日志,崩溃后回放以达成一致。
  • 数据写入模式:
    • data=ordered(默认):数据先落盘,再提交相关元数据到日志;兼顾一致性与性能。
    • data=writeback:元数据仍写日志,但数据写入与日志顺序不强制;性能高、崩溃后可能出现旧数据。
    • data=journal:数据与元数据都写日志,再落盘;一致性最强、代价最高。
  • 校验:metadata_csum 为元数据与日志提供校验,降低静默损坏风险。

3.7 空间分配与预分配

  • 延迟分配(delalloc):推迟选择物理块,待写入实际发生与页面回写时统一分配,更易做大块连续分配。
  • 多块分配(mballoc):一次性按范围为大文件分配连续 extents。
  • 预分配(fallocate):提前保留空间并建立 extents,减少后续碎片。

3.8 从分区到文件内容:元数据路径清单

以下以“读取某文件内容”为例,列出从分区开始到数据块间所经的关键元数据:

  1. 分区层(MBR/GPT)
    • GPT 分区条目记录:分区类型 GUID(Linux FS 类型)、起始/结束 LBA、分区 UUID/名称。
    • 起始 LBA → 分区起点(通常 1MiB 对齐)。
  2. 分区起点到超级块
    • 偏移 0–1023B:保留区/引导代码区域(通常未用)。
    • 偏移 1024B:ext4 超级块,含:
      • 块大小、inode 大小、每组块数/每组 inode 数、特性位(compat/incompat/ro_compat)。
      • 文件系统 UUID、卷标、最近挂载/检查时间、错误处理策略等。
  3. 组描述符表(GDT)
    • 每个块组的描述:块位图所在块、inode 位图所在块、inode 表起始块、空闲块/空闲 inode 计数等。
  4. 目标目录路径解析
    • 从根目录 inode 出发:
      • 读取目录的数据块或 HTree 索引块,依据哈希/名称定位下一层目录项。
      • 目录项给出目标文件的 inode 号。
  5. 目标文件 inode
    • 读取 inode 表中该 inode 记录:权限、大小、时间戳、链接计数、标志、xattr 指针。
    • 如果启用 fscrypt 且该 inode 加密:xattr 中包含加密上下文;需密钥解密数据。
  6. 数据定位(extents)
    • 读取 inode 内的 extent 树:
      • 若深度>0:遍历索引节点,直至叶子 extents。
      • 叶子 extent 将“文件逻辑块区间 → 物理块区间”映射。
    • 如启用 inline_data 且文件很小:数据可能直接在 inode 内,无需独立数据块。
  7. 数据块读取
    • 根据映射读取对应物理块;若启用加密/verity/压缩(若支持),在读取路径中对应解密/验证/解压。
  8. 日志与一致性(读取时通常不参与)
    • 正常读取不访问日志;崩溃恢复时,JBD2 回放日志以保证元数据一致。

3.9 空间占用、稀疏文件与观测

  • 稀疏文件:未写入的逻辑空间不分配块;stat/ls 显示逻辑大小,du 显示实际占用更小。
  • filefrag 可以查看文件的 extent/碎片情况;大顺序 extent 有利于性能。
  • xattrs/ACL/SELinux 标签等元数据占用 inode/xattr 空间,必要时会使用额外 xattr 块。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment