当前位置: 首页 > 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.cnnetsun.cn/news/2907397.html

相关文章:

  • 【毕业设计】基于 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模组管理:新手的终极完整指南
  • MC56F8458x DSC芯片配置与时钟系统实战指南