C++ / a working model

146 / 163   ·   C11   ·   约 8 分钟

崩溃一致性:FSCK 与日志

先记住这句话

文件系统在磁盘上维护 inode、位图和数据块,一次操作常需多次写入。崩溃可能使状态只部分更新,产生不一致。本章用原创讲解介绍 fsck 事后扫描修复,以及日志(预写日志)如何用少量开销实现快速恢复。

本篇内容
  1. 持久化更新中的崩溃问题
  2. 用文件系统检查程序事后修复
  3. 日志:先写意图再改真实结构
  4. 日志的几种常见模式
  5. 运行示例
  6. 动手练习

官方章节 PDF

持久化更新中的崩溃问题

磁盘一次只完成一块写入。追加文件块时必须改 inode、数据位图和新数据块。若崩溃发生在这些写入之间,可能出现位图与 inode 不符、空间泄漏或读到旧垃圾内容。理想情况是整个操作原子完成,从一种一致磁盘镜像直接到达另一种。

用文件系统检查程序事后修复

早期做法是允许不一致出现,重启后再运行 fsck 一类工具。检查程序遍历全部 inode、位图和目录,找出不匹配项并修正。方法实现简单,但大容量磁盘扫描耗时很长,恢复期间文件系统不可用。

日志:先写意图再改真实结构

日志把一次操作的全部意图先追加到专用日志区并强制落盘,然后再修改真正的 inode、位图和数据。崩溃后只需扫描日志,重放已提交事务或丢弃未提交事务,即可迅速回到一致状态,无需全盘扫描。

日志的几种常见模式

数据日志把用户数据和元数据都写入日志,一致性最强但写放大明显。元数据日志只记录元数据,数据直接落盘,崩溃后可能读到旧内容。有序模式保证数据先于对应元数据提交。实现时需在性能、空间占用和安全保证之间权衡。

常见误区

  • 误以为磁盘能原子完成多块写入
  • 日志提交前未强制刷盘,导致日志自身不完整
  • 只修元数据而忽略用户可能读到垃圾数据

运行一个例子

最低标准 C11 · 完整程序 · 下载 .c

#include <stdio.h>

int main(void) {
    printf("Tiny file-system crash-consistency demo\n");
    printf("Operation: append one data block (update inode, bitmap, data)\n\n");
    printf("Crash after 1 write:\n");
    printf("  data only     -> write lost, still consistent\n");
    printf("  inode only    -> pointer to garbage, bitmap mismatch\n");
    printf("  bitmap only   -> leaked block, no owner\n\n");
    printf("Crash after 2 writes:\n");
    printf("  inode+bitmap  -> metadata OK, garbage data\n");
    printf("  inode+data    -> bitmap mismatch\n");
    printf("  bitmap+data   -> leaked block\n\n");
    printf("Journaling solution: write transaction to log, commit, then checkpoint.\n");
    printf("Recovery: replay committed transactions only.\n");
    return 0;
}

在本地编译

gcc -std=c11 -Wall -Wextra -Wpedantic -Werror ostep-42-journaling.c -o example && ./example

预期结果

Tiny file-system crash-consistency demo
Operation: append one data block (update inode, bitmap, data)

Crash after 1 write:
  data only     -> write lost, still consistent
  inode only    -> pointer to garbage, bitmap mismatch
  bitmap only   -> leaked block, no owner

Crash after 2 writes:
  inode+bitmap  -> metadata OK, garbage data
  inode+data    -> bitmap mismatch
  bitmap+data   -> leaked block

Journaling solution: write transaction to log, commit, then checkpoint.
Recovery: replay committed transactions only.

CHECK YOUR UNDERSTANDING

合上答案,试着解释。

追加一块数据时,若 inode 和数据块已写入但位图未更新,磁盘上会出现什么不一致?fsck 与日志分别如何处理?

查看参考答案

inode 指向已使用的块,位图却仍标记为空闲。fsck 扫描后会把位图改成与 inode 一致。日志事先记录完整事务,崩溃后要么三者一起重放,要么全部回滚,不会留下这种中间状态。

继续查证

标准草案与官方章节会更新;版本标记只说明示例最低要求。

回到目录