146 / 163 · C11 · 约 8 分钟
崩溃一致性:FSCK 与日志
文件系统在磁盘上维护 inode、位图和数据块,一次操作常需多次写入。崩溃可能使状态只部分更新,产生不一致。本章用原创讲解介绍 fsck 事后扫描修复,以及日志(预写日志)如何用少量开销实现快速恢复。
持久化更新中的崩溃问题
磁盘一次只完成一块写入。追加文件块时必须改 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 一致。日志事先记录完整事务,崩溃后要么三者一起重放,要么全部回滚,不会留下这种中间状态。
继续查证
标准草案与官方章节会更新;版本标记只说明示例最低要求。