2. 日志系统基础:日志的目的、写前日志(WAL)原理、崩溃一致性保证

2.1 为什么文件系统需要日志?

文件系统在更新磁盘元数据(如 inode、目录项、位图)时,通常涉及多个独立的写操作。如果系统在写操作序列中间发生崩溃(如断电、内核 panic),磁盘上的元数据将处于不一致状态,可能导致数据丢失、目录损坏甚至整个文件系统无法挂载。

日志(Journaling)的核心目的正是为了解决这一问题:在真正修改磁盘元数据之前,先将本次更新操作以“事务”的形式记录到日志区域。一旦日志写入成功,即使后续写操作失败,系统在恢复时也能通过重放(Replay)日志,将文件系统恢复到一致状态。

关键认知: 日志并不直接保护用户数据(data)的完整性,而是保护元数据(metadata)的一致性。对于数据完整性,需要额外的 Data Journaling 或 Checksum 机制。

2.2 写前日志(Write-Ahead Logging, WAL)原理

WAL 是日志系统的核心设计原则,其基本思想是:在将修改写入磁盘上的最终位置之前,先将修改记录写入一个持久化的日志文件中。Ext4 的日志系统(JBD2)严格遵循 WAL 协议。

WAL 的三条基本规则

  1. 日志先行: 事务的日志记录(描述修改内容)必须在对应的数据块写入磁盘之前落盘。
  2. 原子提交: 一个事务的所有日志记录要么全部写入日志,要么全部不写,不能出现部分写入的情况。
  3. 幂等重放: 日志记录的操作必须是幂等的,即重放多次与重放一次的效果相同,避免重复执行导致数据损坏。
WAL 工作流程(简化):
1. 应用程序发起写操作 → 2. 文件系统将修改记录写入日志缓冲区 → 3. 日志缓冲区落盘(Commit) → 4. 将修改写入磁盘数据区(Checkpoint) → 5. 日志记录可被回收。
写前日志(WAL)流程图 应用写请求 日志缓冲区(内存) 日志磁盘区域 数据磁盘区域 日志回收(可覆盖) ① 记录修改 ② 日志落盘 ③ 数据写入 ④ 回收日志 关键顺序:日志落盘(②)必须在数据写入(③)之前完成,这是 WAL 的核心保证。 崩溃恢复时,系统扫描日志区域,重放已提交但未写入数据区的修改。

2.3 崩溃一致性保证

崩溃一致性(Crash Consistency)是指文件系统在任意时刻发生系统崩溃后,通过某种机制能够恢复到逻辑上一致的状态,而无需执行完整的文件系统检查(fsck)。日志系统通过以下机制实现这一目标:

事务的原子性与提交点

JBD2 将一组相关的元数据更新打包成一个事务(Transaction)。事务的生命周期包括:

阶段 描述 崩溃影响
运行(Running) 事务正在收集修改,日志记录写入内存缓冲区 未提交,直接丢弃,文件系统状态不变
提交(Committing) 日志缓冲区落盘,写入提交块(Commit Block) 若提交块未写完,事务视为未提交,丢弃
检查点(Checkpoint) 将修改从日志写入磁盘数据区 若崩溃,恢复时重放日志,重新执行检查点
完成(Finished) 数据已落盘,日志记录可被回收 无影响,文件系统已一致
重要: 提交块(Commit Block)是事务原子性的关键标志。JBD2 在日志末尾写入一个包含事务 ID 和校验和的提交块。恢复时,只有找到完整提交块的事务才会被重放。这确保了“要么全部提交,要么全部丢弃”的原子语义。

恢复流程(Recovery)

当系统在崩溃后重启并挂载文件系统时,JBD2 会自动执行恢复流程:

  1. 扫描日志: 从日志头部开始扫描,找到所有已提交的事务(包含有效提交块)。
  2. 重放(Replay): 按事务顺序,将日志中的修改重新应用到磁盘数据区。由于 WAL 保证日志先于数据落盘,重放操作不会破坏已有数据。
  3. 清理: 重放完成后,日志区域被标记为可回收,文件系统进入一致状态。
崩溃恢复流程图 系统崩溃 挂载文件系统 扫描日志区域 重放已提交事务 文件系统一致 找到提交块 恢复过程对用户透明,通常只需几秒到几十秒,远快于完整的 fsck 扫描。

2.4 日志模式对比

Ext4 支持三种日志模式,它们在性能与一致性保证上存在差异:

模式 日志内容 一致性保证 性能 适用场景
Journal(全日志) 元数据 + 数据块 最高(元数据+数据一致) 最慢(数据双写) 高可靠性要求,数据完整性敏感
Ordered(有序,默认) 仅元数据,但保证数据先落盘 高(元数据一致,数据可能丢失) 较快 通用场景,兼顾性能与安全
Writeback(回写) 仅元数据,不保证数据顺序 低(元数据一致,数据可能不一致) 最快 性能优先,可容忍数据异常
性能与安全权衡: Ordered 模式是 Ext4 的默认选项,它在大多数场景下提供了足够的崩溃一致性保证,同时避免了 Journal 模式的数据双写开销。Writeback 模式虽然快,但在崩溃后可能出现“文件内容包含旧数据”或“指向未写入数据”的情况,仅建议在只读或临时文件系统上使用。

2.5 小结

  • 日志的目的: 保证元数据更新在崩溃后仍能保持一致性,避免 fsck 全盘扫描。
  • WAL 原理: 日志先行、原子提交、幂等重放,确保崩溃恢复的可信性。
  • 崩溃一致性: 通过事务提交点(Commit Block)和恢复重放机制实现。
  • 模式选择: Ordered 模式是默认推荐,Journal 模式提供最高保证,Writeback 模式追求极致性能。