当前位置: 首页 > news >正文

Linux jbd2_journal_recover日志恢复与superblock标记

Linux jbd2_journal_recover日志恢复与superblock标记

jbd2日志恢复在文件系统mount时由jbd2_journal_recover()触发,其核心任务是从日志磁盘区域中扫描已提交但尚未写入文件系统元数据区域的事务,并重放这些事务的元数据块。

日志恢复的入口函数是jbd2_journal_load(),它在mount流程中调用:

int jbd2_journal_load(journal_t *journal)
{
int err;
journal_superblock_t *sb;

err = jbd2_journal_read_superblock(journal, GFP_KERNEL);
if (err)
return err;

sb = journal->j_superblock;
journal->j_tail = be32_to_cpu(sb->s_start);
journal->j_tail_sequence = be32_to_cpu(sb->s_sequence);
journal->j_first = be32_to_cpu(sb->s_first);
journal->j_last = be32_to_cpu(sb->s_maxlen);

if (journal->j_tail_sequence != be32_to_cpu(sb->s_sequence)) {
err = jbd2_journal_recover(journal);
if (err)
return err;
}

err = jbd2_journal_bmap(journal, 0, &journal->j_blk_offset);
...
return 0;
}

函数首先读取journal superblock,从中获取以下关键字段:s_start(日志起始块的物理块号)、s_sequence(当前日志序列号)、s_first和s_maxlen(日志区域的物理范围)。如果s_sequence不等于j_tail_sequence,表示日志不为空,需要执行恢复。

jbd2_journal_recover()调用do_one_pass()执行实际的扫描和重放操作:

int jbd2_journal_recover(journal_t *journal)
{
struct recovery_info info;
int ret;

memset(&info, 0, sizeof(info));

ret = do_one_pass(journal, &info, PASS_SCAN);
if (ret)
return ret;

jbd2_clear_revoke(journal, &info);

ret = do_one_pass(journal, &info, PASS_REPLAY);
if (ret)
return ret;

jbd2_clear_revoke(journal, &info);

ret = do_one_pass(journal, &info, PASS_REVOKE);
if (ret)
return ret;

jbd2_clear_revoke(journal, &info);

return 0;
}

恢复过程分三个阶段(pass)。PASS_SCAN:从日志尾部开始向前扫描所有日志块,构建事务结构,收集revoke记录。PASS_REPLAY:按事务顺序重放所有已提交的元数据块。PASS_REVOKE:处理revoke记录,跳过被撤销的日志块重放。

do_one_pass()的扫描逻辑如下:

static int do_one_pass(journal_t *journal,
struct recovery_info *info,
enum passtype pass)
{
unsigned int first_commit_ID;
unsigned int next_commit_ID;
unsigned long long next_log_block;
int err, success = 0;
journal_superblock_t *sb;
struct buffer_head *bh;
unsigned int sequence;
int blocktype;

sb = journal->j_superblock;
next_commit_ID = info->end_commit_id;
next_log_block = info->end_transaction;

for (;;) {
bh = __jbd2_journal_read_next_block(journal, &next_log_block,
&next_commit_ID);
if (!bh)
break;

sequence = be32_to_cpu(bh->b_blocknr);
blocktype = be32_to_cpu(((journal_header_t *)bh->b_data)->h_blocktype);

switch (blocktype) {
case JBD2_DESCRIPTOR_BLOCK:
if (pass == PASS_REPLAY) {
err = jbd2_journal_replay_descriptor(journal, bh,
next_log_block);
}
break;

case JBD2_COMMIT_BLOCK:
info->end_commit_id = next_commit_ID;
break;

case JBD2_REVOKE_BLOCK:
if (pass == PASS_REVOKE) {
err = jbd2_journal_scan_revoke_record(journal, bh,
next_log_block);
}
break;
}
brelse(bh);
}
return 0;
}

扫描器从日志中逐个读取块,通过h_blocktype字段判断块类型。JBD2_DESCRIPTOR_BLOCK是描述符块,包含本次事务中所有元数据块的UUID、块号列表和校验信息。JBD2_COMMIT_BLOCK是提交块,包含事务的checksum和flags,标记事务成功提交。JBD2_REVOKE_BLOCK是撤销块,列出了不需要重放的块号列表。

在实际重放阶段,jbd2_journal_replay_descriptor()根据描述符块中的条目逐个处理:

int jbd2_journal_replay_descriptor(journal_t *journal,
struct buffer_head *descriptor,
unsigned long long next_log_block)
{
int i;
int err = 0;
struct buffer_head *bh;
unsigned long blocknr;
journal_block_tag_t *tag;
int flags;

tag = (journal_block_tag_t *)&descriptor->b_data[sizeof(journal_header_t)];
for (i = 0; i < journal->j_blocksize / sizeof(journal_block_tag_t); i++) {
blocknr = be32_to_cpu(tag->t_blocknr);
flags = tag->t_flags;

if (flags & JBD2_FLAG_SAME_UUID) {
bh = __jbd2_journal_get_descriptor_buffer(journal, blocknr);
if (!bh)
return -EIO;

err = jbd2_journal_replay_block(journal, bh, next_log_block);
if (err) {
brelse(bh);
return err;
}

if (flags & JBD2_FLAG_LAST_TAG)
break;
brelse(bh);
}
tag++;
}
return 0;
}

每个描述符块包含多个tag条目,每个tag对应一个日志数据块。t_blocknr是目标块号(文件系统元数据块的物理位置),t_flags包含JBD2_FLAG_SAME_UUID和JBD2_FLAG_LAST_TAG等标志。jbd2_journal_replay_block()将日志块的数据复制到目标元数据块:

int jbd2_journal_replay_block(journal_t *journal,
struct buffer_head *obh,
unsigned long long next_log_block)
{
struct buffer_head *nbh;
int err;

nbh = __getblk(journal->j_fs_dev, next_log_block,
journal->j_blocksize);
lock_buffer(nbh);
memcpy(nbh->b_data, obh->b_data, journal->j_blocksize);
set_buffer_uptodate(nbh);
mark_buffer_dirty(nbh);
unlock_buffer(nbh);
sync_dirty_buffer(nbh);
brelse(nbh);
return 0;
}

恢复完成后,jbd2_journal_recover()更新journal superblock的s_start和s_sequence字段,标记日志为空:

void jbd2_journal_update_sb_log_tail(journal_t *journal,
tid_t tail_tid,
unsigned long long tail_block,
int write_op)
{
journal_superblock_t *sb = journal->j_superblock;
sb->s_sequence = cpu_to_be32(tail_tid);
sb->s_start = cpu_to_be32(tail_block);
jbd2_journal_write_superblock(journal, write_op);
}

s_start被设置为0表示日志已清空,s_sequence更新为最后一个已提交事务的序列号。此时文件系统可以直接挂载,不需要再次恢复。

jbd2的superblock还包含了s_errno字段用于记录上一次mount时的错误码,以及s_feature_compat/s_feature_incompat/s_feature_ro_compat三个特性字段,用于控制日志格式的向后兼容性。当jbd2检测到未知的incompat特性位时,拒绝mount以防止数据损坏。

http://www.cnnetsun.cn/news/2907398.html

相关文章:

  • Linux jbd2_journal_commit_transaction日志提交与forget链表
  • 【毕业设计】基于 SpringBoot 的数据资产备案与登记管理系统研究 适配企业数字化转型的数据资产登记系统开发与实践(源码+文档+远程调试,全bao定制等)
  • 深入解析MC68377 CTM9 DASM:输出比较与PWM模式实战指南
  • 终极Laravel项目搭建工具:Laravel Installer核心功能详解
  • 告别手动配置!用Advanced Installer 15.7把SpringBoot Jar包一键打包成Windows服务(附Java环境自动检测)
  • 从零到实战:用Kalibr和ROS Melodic标定你的RealSense D435i(附标定板生成与数据录制技巧)
  • 实战指南:在PyTorch/TensorFlow项目中,用LIME和SHAP给你的‘黑箱’模型做个‘X光’检查
  • OpenClaw 企业级 Agent 平台技术方案
  • 2026图片在线去水印网站安全无广告怎么找?视频在线去水印平台免费推荐
  • Speechless:无需登录一键备份微博到PDF的终极解决方案
  • 在iPhone上运行BLOOM模型:Bloomer iOS应用开发入门指南
  • Skinny Bones Jekyll Starter完全解析:10个核心功能让你轻松定制网站
  • ComfyUI-VideoHelperSuite:解决AI视频工作流三大痛点的终极方案
  • ComfyUI LLM Party终极指南:快速搭建AI工作流的10个核心工具详解
  • ChemCrow化学AI助手:让复杂化学分析变得像聊天一样简单
  • Emacs-for-Python 核心功能详解:Ropemacs 重构工具完全解析 [特殊字符]
  • 电气 / 机械工程师必备:工程数学计算软件 Mathcad Prime 入门介绍
  • 云顶之弈策略博弈中信息优势的构建:TFT Overlay实战深度解析
  • AI动态简报之技术前沿篇(2026.06.13)
  • 如何一键清理Windows 11系统臃肿?Win11Debloat终极优化指南
  • PacketEvents事件系统完全指南:从基础监听器到高级事件处理
  • Hi3531A开发板UART1/2/3硬件接线+驱动编译+通信测试全链路实操包
  • 用STM32CubeMX和HAL库快速驱动GM65模块:一个智能快递柜扫码开箱的实战项目
  • Stable Diffusion 2.1模型训练原理:深入理解潜在扩散模型工作机制
  • ComfyUI-KJNodes:AI工作流效率优化的终极解决方案
  • 终极指南:如何用BERTScore轻松评估文本生成质量?完整教程与实用技巧
  • MC9S08QE128 Flash内存编程实战:从寄存器配置到安全机制详解
  • PyTorch-NPU/dpt_large与其他深度估计模型的对比分析
  • BilibiliCacheVideoMerge:安卓用户的B站缓存合并终极解决方案
  • 如何快速掌握XCOM 2模组管理:新手的终极完整指南