当前位置: 首页 > 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.jsqmd.com/news/1006602/

相关文章:

  • UUV Simulator终极指南:快速构建高保真水下机器人仿真系统
  • 深入解析MC9328MX1 UART驱动:从寄存器配置到中断处理的嵌入式实战
  • 2026年6月最新|充气帐篷厂家哪家好?专业生产厂家实力口碑排行推荐 - 商业新知
  • 革命性开源5G测试平台:UERANSIM如何让5G研发变得简单高效
  • 【Springboot毕设全套源码+文档】基于Spring Boot的奖学金评定管理系统设计与实现(丰富项目+远程调试+讲解+定制)
  • 2026甄选郑州名酒回收实体店联系方式,专业鉴定与诚信回收的可靠选择 - 企业推荐官【官方】
  • 漳州报名 CPPM 注册采购经理哪家靠谱?机构选择避坑指南 - 众智商学院课程中心
  • 2026湖北电大中专报名条件及报考须知|湖北成人中专报考答疑 - 善良的阿良
  • 2026年6月南通劳保手套工厂排行:服务与品质双维度深度盘点 - 奔跑123
  • 刷CF #1900 二周目
  • Python 高手编程系列三千三百七十八:构建自己的文档集
  • 2026年6月国内松木镜框油画布框套装定制服务商排行top5,资质与专业评测推荐 - 奔跑123
  • 如何快速部署AI模型到嵌入式设备:5大实用技巧与RKNN-Toolkit2终极指南
  • 2026温州打捞队真实记录:本地榜单TOP1,这些水域都靠他们 - 速递信息
  • Linux jbd2_journal_commit_transaction日志提交与forget链表
  • 从ImageNet-22k到ImageNet-1k:swinv2_base_window12to16_192to256.ms_in22k_ft_in1k训练策略分析
  • 2026 青岛汽车音响改装靠谱度榜首:鼎峰汇汽车音响,被低估的技术标杆 - 汽车音响改装
  • 3分钟掌握Blender建筑生成:Building Tools终极指南
  • 鸿蒙原生应用实战(五):教程、主题与项目总结 — 从开发到上线的完整回顾
  • 3种高效WebRTC流媒体架构方案对比与Metahuman-Stream部署优化指南
  • League Akari:本地化英雄联盟智能助手完整实用指南
  • Visual Syslog Server:为Windows系统打造的专业级集中日志管理解决方案
  • 2026西安钻石回收翘楚,本地赛道顶流王机构测评 - 讯息早知道
  • 别再乱用快照了!QEMU磁盘快照和检查点快照的保姆级区别与实战(Windows+Debian)
  • texture-vs-shape项目FAQ全解答:从刺激集获取到模型评估的常见问题
  • DLSS Swapper终极指南:智能游戏性能优化方案
  • 2026石家庄翡翠回收深度实测:七家机构种水色工专项横评 - 薛定谔的梨花猫
  • 2026 南宁装修公司哪家靠谱?实测十大口碑品牌汇总 - 装修新知
  • 华浙培训・浙经院高复班(下沙)电话号码给我一下 - 弱书讲升学
  • Python 高手编程系列三千三百七十六:章节结构