下面概括机械硬盘/SSD在操作系统视角下的寻址与I/O基本面,便于后续讨论文件系统、分区和性能优化。
统一寻址:现代系统用 LBA(Logical Block Addressing)按块编号访问,不再用CHS。 基本单元:逻辑扇区通常为 512B(或4K);物理扇区常见为 4K(Advanced Format)。 对齐很关键:分区与文件系统数据结构应按 4K(甚至1MB)对齐,避免读-改-写(RMW)罚时。 顺序优于随机:磁盘顺序访问远快于随机;队列与调度可缓解随机I/O开销。 持久性与一致性:缓存写回需配合刷新(flush/fsync),以确保掉电后数据落盘。 健康与重映射:坏扇区会被设备透明重映射,但可能引入不可预期的长延迟。
- 逻辑扇区与物理扇区 物理扇区(Physical Sector) 磁盘或SSD内部实际读写的最小物理块,现代硬盘多为 4KiB(4Kn)。 逻辑扇区(Logical Sector) 操作系统看到的最小地址单元,常见为 512B 或 4KiB。 512e(512-byte emulation):设备物理4K,但对外“模拟”512B逻辑扇区。未对齐到4K的写入会触发读-改-写(RMW)流程,增加延迟与写放大。 原子性 通常以“逻辑扇区大小”为原子写单元(崩溃不应撕裂跨扇区的部分,仍需文件系统/日志机制保障更高层一致性)。
- 寻址方式:CHS 与 LBA CHS(Cylinder-Head-Sector) 早期按柱面/磁头/扇区定位,已不适用于现代设备容量与内部布局。 LBA(Logical Block Addressing) 用连续编号的“块号”表示扇区位置,LBA从0开始递增。 操作系统/控制器将 LBA 映射到设备内部的实际位置与闪存/磁道布局。
- 扇区大小与对齐 扇区大小 常见组合:逻辑512B + 物理4K(512e),或逻辑/物理同为4K(4Kn)。 对齐原则 分区起始 LBA 应对齐到 4K,常用“1MB 对齐”(起始偏移 1,048,576 字节),可同时满足4K与多数RAID条带对齐需求。 文件系统元数据、数据区、日志/日志组建议保持4K或更大粒度对齐,降低RMW与写放大。 检查与实践 Windows 默认新建分区采用 1MB 对齐;跨平台迁移或历史分区需复核。
- I/O 路径与调度(简版) 路径概览 应用 → 文件系统(缓存/日志) → 块层/队列 → 主机控制器/驱动 → 设备固件 → 介质。 调度与队列 HDD:调度倾向合并/重排请求以减少寻道(顺序化有益)。 SSD/NVMe:多队列并行(提交/完成队列),性能与队列深度、并发度相关;仍建议合并小IO以降低开销。
- 顺序访问与随机访问 顺序访问 HDD:显著减少寻道,吞吐与延迟最优。 SSD:也可减少控制器/总线与元数据开销,提升写入放大控制。 随机访问 HDD:寻道主导延迟,性能下降明显。 SSD:控制器与并行度可缓解,但小随机写仍可能触发擦写与GC。
- 分区表与起始扇区(MBR/GPT) MBR 传统分区表,位于 LBA0;最大2TB(32位LBA)限制;最多4个主分区(或扩展分区)。 GPT 现代分区表,主头位于 LBA1,支持更大地址空间与更多分区;包含备份头。 兼容性:有 Protective MBR 以避免旧工具误操作。 起始扇区与对齐 GPT下常见第一个分区从 1MB 边界开始,对齐4K且预留空间给对齐和元数据。
- 缓存、刷写与顺序保证 主机页缓存与设备缓存 写入先进入内存页缓存与/或设备缓存(write-back),并非立刻落盘。 刷写与持久化 应用层应在关键路径调用刷新原语确保数据持久(例如:Linux fsync/fdatasync;Windows FlushFileBuffers)。 写屏障/强制单元访问(FUA)用于确保顺序与落盘;设备与驱动需正确实现。 掉电保护 企业级设备可能具备掉电保护电容,写回策略更安全;消费级设备建议应用与文件系统更保守地刷写关键数据。
- 坏扇区与重映射 透明重映射 设备固件将坏扇区重映射到预留扇区,LBA视图不变。 性能与可观测性 重映射可能引入个别请求的极长延迟(如反复重试后成功/失败)。 通过 S.M.A.R.T. 指标(如 Reallocated Sector Count、Current Pending Sector)可观察健康趋势,必要时尽早更换。
- 常用术语小词典 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.:设备健康监测指标集合。
- 位置与大小:磁盘第 0 扇区(通常 512B)。
- 布局:
- 启动代码区(Bootstrap)
- 分区表(4 个主分区条目,每条 16B,共 64B)
- 结束签名 0x55AA
- 能力与限制:
- 最多 4 个主分区;可用扩展分区容纳更多逻辑分区
- 使用 32 位 LBA 与传统 512B 扇区时,容量上限约 2 TiB
- 无冗余校验,元数据易受损
- 启动路径(典型 BIOS):BIOS -> MBR -> 分区引导扇区(VBR) -> 引导加载器/内核
- 兼容头:LBA 0 放置 Protective MBR(防止旧工具误覆盖)
- 主结构:
- GPT Header(LBA 1)
- 分区条目数组(紧随其后,默认 128 项、每项 128B,可扩展)
- 备份 GPT Header 与条目数组位于磁盘末尾(冗余)
- 特性:
- 64 位 LBA,支持超大容量
- CRC32 校验,元数据冗余更安全
- 每个分区有“分区类型 GUID”和“唯一分区 GUID”
- UEFI 原生支持,仍可与传统 MBR 共存(Protective MBR)
- GPT 容量更大、元数据具校验与冗余、分区数量不再受 4 个主分区限制,是现代首选。
- 旧系统或特定场景可能仍使用 MBR;新部署建议优先 GPT。
- 卷引导扇区(VBR / $Boot)
- $MFT(主文件表)与 $MFTMirr(镜像)
- $LogFile(日志)、$Bitmap(簇位图)、$BadClus(坏簇)、$UpCase、$Secure 等
- 一切皆文件:这些“元文件”自身也是 NTFS 文件
- 定义:文件系统最小分配单位,常见为 4 KiB,也可按格式化时选项调整。
- 用户视角:
- Windows 格式化时可选“分配单元大小”
- 资源管理器显示“大小”与“占用空间”
- 文件大小 vs 占用空间:
- 占用空间按簇对齐:哪怕只写入 1B,也至少占用 1 个簇(内部碎片)
- 压缩文件/稀疏文件会使“占用空间”小于“大小”
- 流(Alternate Data Streams)与元数据可能影响统计口径
- NTFS 把文件表示为一系列“属性”(Attribute),例如:
- $STANDARD_INFORMATION、$FILE_NAME、$SECURITY_DESCRIPTOR
- $DATA(文件内容,既可驻留也可非驻留)
- 当 $DATA 属性为“驻留(Resident)”时,文件内容直接存放在 MFT 记录里,不分配簇。
- 这类小文件在资源管理器中“占用空间”可能显示为 0 KB
- 实际仍占用 MFT 记录空间(典型 1 KiB 或 4 KiB),但该空间不计入“占用空间(按簇计)”
- 每个文件/目录对应一个或多个 MFT 记录(记录中是属性列表)
- 常见属性:
- $DATA:驻留/非驻留数据
- $INDEX_ROOT / $INDEX_ALLOCATION:目录索引(B+ 树样式)
- $ATTRIBUTE_LIST:跨记录/文件拼接属性
- $Bitmap 跟踪簇分配,$MFT 自身也会扩展并分配簇存放非驻留部分
- 硬链接(hard link):
- 同一卷内为同一文件内容创建多个目录项;共享同一 MFT 条目/数据
- 删除其中一个路径不影响其他路径,引用计数为 0 才真正释放
- 仅对文件有效(目录硬链接仅系统内部使用)
- 符号链接(软链接,symlink):
- 以路径为目标,可指向文件或目录,可跨卷
- 需要相应权限或开启“开发者模式”
- 目标不存在时为“悬挂链接”
- 连接点/目录联接(junction):
- 基于重解析点的目录级跳转,多用于跨目录/卷重定位
- 快捷方式(.lnk):
- 用户层文件,不是文件系统级链接
- NTFS 的访问控制(ACL)在文件系统层由驱动强制。
- 以原始方式读取设备(例如 \.\PhysicalDriveN,或以足够权限打开原始卷句柄)会绕过文件级 ACL 检查,直接获取原始扇区数据。
- 前提:需要管理员/备份权限,可能需要锁定/脱机卷以避免并发一致性问题
- 加密限制:
- BitLocker 未解锁时,原始读到的是密文
- EFS 针对文件级加密,未持有密钥时即使通过备份语义也无法解密内容
- 备份相关 API 可在权限允许下绕过 ACL 读取文件,但仍受加密与审计约束
- 分区内起始布局:
- 预留区:从分区起始偏移 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,可选)。
- 块大小:常见为 4 KiB(也支持 1/2 KiB 等,取决于创建时参数与平台)。
- 分配粒度:以块为最小分配单位,文件内容通过“extent”映射逻辑块区间到物理块区间。
- 碎片:
- 内部碎片:文件大小不是块大小整数倍时最后一块未用部分。
- 外部碎片:文件的块在磁盘上不连续;extents 与 mballoc 有助于降低。
- 当启用 inline_data 且文件很小(数百字节量级),内容可直接存放在 inode 的 i_block/xattr 空间里,不占用单独数据块。
- 类似“驻留数据”的效果:
- 优点:减少小文件的块分配与寻址开销,提升小 I/O 性能。
- 观测:逻辑大小>0,但磁盘实际占用块可能为 0(除 inode 表本身)。
- 符号链接的“快速 symlink”也可将短目标路径直接放在 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 索引(哈希+树形索引)加速查找;叶子仍是成批的目录项块。
- 硬链接(hard link):
- 同一文件系统内为同一 inode 建立多个目录项;inode 链接计数控制释放时机。
- 不能对目录创建普通硬链接(. 与 .. 为特殊保留)。
- 符号链接(symlink,软链接):
- 保存目标路径,可跨文件系统、可指向目录;短路径可 inline 存储。
- 说明:
- bind mount 属于 VFS/挂载层语义,并非 ext4 链接。
- 新版内核/工具链可选提供 reflink/克隆(写时复制),需内核与 mkfs/mount 选项支持。
- 日志位置:内置日志文件(历史上常为隐藏 inode),或外置独立设备/分区。
- 事务:元数据修改被打包为事务写入日志,崩溃后回放以达成一致。
- 数据写入模式:
- data=ordered(默认):数据先落盘,再提交相关元数据到日志;兼顾一致性与性能。
- data=writeback:元数据仍写日志,但数据写入与日志顺序不强制;性能高、崩溃后可能出现旧数据。
- data=journal:数据与元数据都写日志,再落盘;一致性最强、代价最高。
- 校验:metadata_csum 为元数据与日志提供校验,降低静默损坏风险。
- 延迟分配(delalloc):推迟选择物理块,待写入实际发生与页面回写时统一分配,更易做大块连续分配。
- 多块分配(mballoc):一次性按范围为大文件分配连续 extents。
- 预分配(fallocate):提前保留空间并建立 extents,减少后续碎片。
以下以“读取某文件内容”为例,列出从分区开始到数据块间所经的关键元数据:
- 分区层(MBR/GPT)
- GPT 分区条目记录:分区类型 GUID(Linux FS 类型)、起始/结束 LBA、分区 UUID/名称。
- 起始 LBA → 分区起点(通常 1MiB 对齐)。
- 分区起点到超级块
- 偏移 0–1023B:保留区/引导代码区域(通常未用)。
- 偏移 1024B:ext4 超级块,含:
- 块大小、inode 大小、每组块数/每组 inode 数、特性位(compat/incompat/ro_compat)。
- 文件系统 UUID、卷标、最近挂载/检查时间、错误处理策略等。
- 组描述符表(GDT)
- 每个块组的描述:块位图所在块、inode 位图所在块、inode 表起始块、空闲块/空闲 inode 计数等。
- 目标目录路径解析
- 从根目录 inode 出发:
- 读取目录的数据块或 HTree 索引块,依据哈希/名称定位下一层目录项。
- 目录项给出目标文件的 inode 号。
- 从根目录 inode 出发:
- 目标文件 inode
- 读取 inode 表中该 inode 记录:权限、大小、时间戳、链接计数、标志、xattr 指针。
- 如果启用 fscrypt 且该 inode 加密:xattr 中包含加密上下文;需密钥解密数据。
- 数据定位(extents)
- 读取 inode 内的 extent 树:
- 若深度>0:遍历索引节点,直至叶子 extents。
- 叶子 extent 将“文件逻辑块区间 → 物理块区间”映射。
- 如启用 inline_data 且文件很小:数据可能直接在 inode 内,无需独立数据块。
- 读取 inode 内的 extent 树:
- 数据块读取
- 根据映射读取对应物理块;若启用加密/verity/压缩(若支持),在读取路径中对应解密/验证/解压。
- 日志与一致性(读取时通常不参与)
- 正常读取不访问日志;崩溃恢复时,JBD2 回放日志以保证元数据一致。
- 稀疏文件:未写入的逻辑空间不分配块;stat/ls 显示逻辑大小,du 显示实际占用更小。
- filefrag 可以查看文件的 extent/碎片情况;大顺序 extent 有利于性能。
- xattrs/ACL/SELinux 标签等元数据占用 inode/xattr 空间,必要时会使用额外 xattr 块。