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

Linux jbd2_journal_commit_transaction日志提交与forget链表

Linux jbd2_journal_commit_transaction日志提交与forget链表

jbd2(Journaling Block Device 2)是ext4等文件系统使用的通用日志层。事务提交由jbd2_journal_commit_transaction()实现,这是jbd2中最复杂的函数之一,负责将运行中的事务写入日志磁盘区域并完成原子更新。

每个事务由struct transaction_s表示,包含多个链表关键链表:t_buffers(元数据缓冲区链表)、t_forget(forget链表)、t_shadow_list、t_iobuf_list等。

事务提交的主路径从jbd2_journal_commit_transaction()开始:

int jbd2_journal_commit_transaction(journal_t *journal)
{
transaction_t *commit_transaction;
struct journal_head *jh;
struct buffer_head *bh;
int ret;
int flags;
DEFINE_WAIT(wait);

commit_transaction = journal->j_running_transaction;
commit_transaction->t_state = T_LOCKED;

spin_lock(&commit_transaction->t_handle_lock);
while (atomic_read(&commit_transaction->t_updates)) {
DEFINE_WAIT(wait);
prepare_to_wait(&journal->j_wait_updates, &wait,
TASK_UNINTERRUPTIBLE);
spin_unlock(&commit_transaction->t_handle_lock);
schedule();
spin_lock(&commit_transaction->t_handle_lock);
finish_wait(&journal->j_wait_updates, &wait);
}
spin_unlock(&commit_transaction->t_handle_lock);

jbd2_journal_write_revoke_records(journal, commit_transaction);

jbd2_journal_submit_data_buffers(journal, commit_transaction);

jbd2_journal_write_metadata_buffer(journal, commit_transaction, &wbuf);

commit_transaction->t_state = T_FLUSH;
journal->j_ committing_transaction = commit_transaction;
... // Journal block tag + descriptor block writing

jbd2_journal_update_sb_log_tail(journal,
journal->j_tail,
journal->j_tail_sequence,
GFP_NOFS);
return 0;
}

第一阶段是T_LOCKED状态。函数等待t_updates原子计数器归零,表示所有正在运行的handle都已结束。然后锁定事务,不允许新的handle加入。随后写入revoke记录(用于取消已提交的日志块重放),标记已删除的块不需要在恢复时重做。

第二阶段是元数据缓冲区的flush写入。jbd2_journal_write_metadata_buffer()对每个脏的journal_head执行操作:

int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
struct journal_head *jh_in)
{
struct page *page;
struct page *new_page;
struct buffer_head *bh_in = jh2bh(jh_in);
char *tmp_buffer;
int done;

new_page = jbd2_alloc(journal->j_blocksize, GFP_NOFS);
tmp_buffer = page_address(new_page);

page = bh_in->b_page;
wait_on_buffer(bh_in);

memcpy(tmp_buffer, page_address(page) + bh_in->b_offset,
journal->j_blocksize);

set_bh_page(bh_in, new_page, 0);
jh_in->b_frozen_data = tmp_buffer;
jh_in->b_committed_data = NULL;

J_ASSERT_JH(jh_in, !jh_in->b_committed_data);
jh_in->b_jlist = BJ_Shadow;
set_buffer_shadow(bh_in);
set_buffer_jwrite(bh_in);
return 0;
}

该函数将元数据缓冲区的数据复制到b_frozen_data(冻结快照),保证日志写入时的数据一致性。原始缓冲区被标记为BJ_Shadow加入t_shadow_list,表示正等待日志提交完成后释放。

forget链表的处理是事务提交中的关键环节。当文件系统删除一个块时,jbd2_journal_forget()将该缓冲区的journal_head加入事务的t_forget链表:

int jbd2_journal_forget(journal_t *journal, struct buffer_head *bh)
{
transaction_t *transaction;
struct journal_head *jh;

jh = jbd2_journal_add_journal_head(bh);
transaction = jh->b_transaction;

if (jh->b_cp_transaction) {
JBUFFER_TRACE(jh, "on cp+unfrozen");
__jbd2_journal_unfile_buffer(jh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
} else if (transaction == journal->j_committing_transaction) {
set_buffer_freed(bh);
jh->b_frozen_data = NULL;
jh->b_committed_data = NULL;
jh->b_next_transaction = NULL;
jh->b_transaction = NULL;
jbd2_journal_put_journal_head(jh);
} else {
if (jh->b_transaction) {
J_ASSERT_JH(jh, jh->b_jlist == BJ_None ||
jh->b_jlist == BJ_SyncData ||
jh->b_jlist == BJ_Metadata);
if (jh->b_jlist != BJ_None)
__jbd2_journal_refile_buffer(jh);
}
set_buffer_freed(bh);
__jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
}
return 0;
}

当buffer被forget后,它被放入事务的BJ_Forget链表(t_forget_list)。在事务提交时,jbd2_journal_commit_transaction()遍历t_forget链表的journal_head,逐个调用jbd2_journal_invalidatepage()或释放buffer:

commit_transaction->t_state = T_COMMIT;
while (commit_transaction->t_forget_list) {
jh = commit_transaction->t_forget_list;
bh = jh2bh(jh);

if (buffer_freed(bh)) {
clear_buffer_freed(bh);
clear_buffer_jbddirty(bh);
J_ASSERT_JH(jh, !buffer_journaled(jh));
J_ASSERT_JH(jh, !buffer_journal_dirty(jh));
J_ASSERT_JH(jh, !buffer_mapped(bh));
if (jh->b_cp_transaction) {
__jbd2_journal_unfile_buffer(jh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
}
} else {
__jbd2_journal_unfile_buffer(jh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
}
}

forget链表中的buffer如果是buffer_freed状态,表示对应的block已经在文件系统层面被释放。日志提交时直接清空相关标记并释放journal_head。如果不是freed状态,表示buffer只是从当前事务解绑,但不释放block本身。

事务提交的最后阶段是T_FLUSH和T_COMMIT。在T_FLUSH阶段,jbd2发出屏障IO请求确保日志块先于数据块落盘。在T_COMMIT阶段,写入commit block(包含事务的checksum和时间戳)完成事务提交。

jbd2_journal_commit_transaction()的最终操作是更新journal superblock中的日志尾部位置:调用jbd2_journal_update_sb_log_tail()将j_tail和j_tail_sequence写入journal superblock,标记已经被checkpoint的日志位置。至此,日志空间可被循环复用。

http://www.jsqmd.com/news/1006587/

相关文章:

  • 从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 高手编程系列三千三百七十六:章节结构
  • 线上虚高报价陷阱拆解,青岛六家正规回收渠道横向对比 - 讯息早知道
  • 别再手动调参了!用Keras+20 Newsgroups数据集5步搞定文本聚类(附完整代码)
  • 2026年浙江AI搜索优化源头厂家深度评测与选型指南 - 品牌报告
  • Aider
  • 2026 年 6 月深圳卫生间阳台屋顶漏水修缮测评 本地三家防水工艺材料质保全方位对比 - 吉修匠
  • OpenHarmony 中 GN 的工作机制 — 总览
  • Java毕设项目:基于 Java 的校园二手资源循环置换系统开发研究 校园二手物品智能置换管理系统 (源码+文档,讲解、调试运行,定制等)
  • Kazumi:3个核心技巧打造流畅弹幕视频体验,彻底告别卡顿与发热
  • 去除水印工具推荐:软件小程序都好用的去水印神器 - 工具软件使用方法推荐
  • 电气 / 机械工程师必备:工程数学计算软件 Mathcad Prime 入门介绍
  • Adobe CC 2019-2023通用权限管理工具终极指南:三步配置完整方法
  • 10个必须掌握的knausj_talon命令,程序员的语音编程效率神器
  • 2026 年 6 月中山黄金回收怎么选?综合评测:三家主流机构专业评定 - zzlzzl6688
  • 基于双SI4463芯片的 AIS 接收机开发
  • 从加法器到ALU:手把手教你用Verilog HDL搭建一个简易CPU核心模块