ext3JBD文档Word格式文档下载.docx
- 文档编号:22558874
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:31
- 大小:430.01KB
ext3JBD文档Word格式文档下载.docx
《ext3JBD文档Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《ext3JBD文档Word格式文档下载.docx(31页珍藏版)》请在冰豆网上搜索。
写回(Writeback)
只有对文件系统元数据的改变才记入日志;
这是在其他日志文件系统发现的方法,也是最快的模式
1.2日志块设备(JBD)
Ext3文件系统本身不处理日志,而是利用日志块设备(JournalingBlockDevice)或叫JBD的通用内核层。
Ext3文件系统调用JDB例程以确保在系统万一出现故障时它的后续操作不会损坏磁盘数据结构。
Ext3与JDB之间的交互本质上基于三个基本单元:
日志记录,原子操作和事务。
日志记录本质上是文件系统将要发出的低级操作的描述。
在某些日志文件系统中,日志记录只包括操作所修改的字节范围及字节在文件系统中的起始位置。
然而,JDB层使用的日志记录由低级操作所修改的整个缓冲区组成。
这种方式可能浪费很多日志空间(例如,当低级操作仅仅改变位图的一个位时),但是,它还是相当快的,因为JBD层直接对缓冲区和缓冲区首部进行操作。
修改文件系统的任一系统调用都通常划分为操纵磁盘数据结构的一系列低级操作。
如果这些低级操作还没有全部完成系统就意外宕机,就会损坏磁盘数据。
为了防止数据损坏,Ext3文件系统必须确保每个系统调用以原子的方式进行处理。
原子操作是对磁盘数据结构的一组低级操作,这组低级操作对应一个单独的高级操作。
出于效率的原因,JBD层对日志的处理采用分组的方法,即把属于几个原子操作处理的日志记录分组放在一个单独的事务中。
此外,与一个处理相关的所有日志记录都必须包含在同一个事务中。
一个事务的所有日志记录都存放在日志的连续块中。
JBD层把每个事务作为整体来处理。
例如,只有当包含在一个事务的日志记录中的所有数据提交给文件系统时才回收该事务所使用的块。
2JBD层
2.1JBD层基本概念
Logrecord
记录文件系统中一个block的改动。
记录格式为journal_block_tag_t结构,存储在日志文件.journal中的discriptorblock。
当JBD层把一个block写入磁盘时,给block对应的bufferhead添加一个journal_head结构。
Atomicoperationhandle
一个systemcall对应的所有logrecord组织在一起,称为一个handle。
为保证文件系统一致性,handle为原子操作,当恢复系统fsck时,要么执行一个handle中的所有logrecord操作,要么一个都不执行。
Transaction
JBD将多个handle组织成一个transaction,transaction中的所有logrecord存储在日志文件的连续的块中。
当一个transaction中的所有logrecord被提交到磁盘上之后,这个transaction占用的block就可以被回收再利用了。
transaction的整个生命历程中的状态和完成的工作如下:
1)T_RUNNING:
表示当前transaction正在运行,可以接收数据到各个链表。
每当发起一个写数据操作的时候,就会启动一个newhandle,newhandle会检查当前是否有runningtransaction,如果有,就向这个transaction里面提交数据。
如果当前没有runningtransaction,就会创建一个新的transaction并置为running状态,向这个transaction提交数据。
细节可以详见start_this_handle()函数
2)T_LOCKED:
transaction的锁,很多时候会用到,比如commit的时候
3)T_RUNDOWN:
4)T_FLUSH:
把ordered模式下的数据刷到硬盘上
5)T_COMMIT:
写日志到硬盘。
此时所有要写到日志的内容已经放到当前transaction的t_buufer链表中了(三种日志模式下要写入的数据和元数据不一样,详见前面的分析,但是所有的内容都放入这个链表),扫描这个链表,把所有内容写入journal.
之后这个transaction会被加入到joural的checkpointlist,在合适的时候把数据或元数据写入硬盘。
可见,checkpoint就是把数据或元数据写入硬盘。
6)T_FINISHED:
这时数据已经写入硬盘,transaction工作完毕,可以从日志中删除了
2.2Jbd数据结构关系
3日志文件格式
日志文件.journal按block来纪录信息,这里有两个层面的含义:
1)日志文件记录的是文件系统中block的改动,而不是文件或者inode的改动;
2)日志文件本身以block为记录单位。
日志文件格式解析如下:
.journal日志文件的第一个block,也就是block0存放的是journalsuperblock,主要纪录了日志文件的blocksize大小、总block数量、第一个log块的块号、日志的第一个transactionID号。
.journal日志文件中所有的block都以一个描述信息为开头,描述信息由journal_header_s结构表示,该结构信息如下:
typedefstructjournal_header_s
{
__u32h_magic;
__u32h_blocktype;
__u32h_sequence;
}journal_header_t;
其中,h_magic为固定值0xc03b3998U,用来判断这个block是否为合法块;
h_sequence为该transaction的ID;
h_blocktype分为5种类型:
#defineJFS_DESCRIPTOR_BLOCK1
#defineJFS_COMMIT_BLOCK2
#defineJFS_SUPERBLOCK_V13
#defineJFS_SUPERBLOCK_V24
#defineJFS_REVOKE_BLOCK5
3.1discriptorblock
每个discriptorblock的开头是journal_header_s结构,紧跟着journal_header_s该结构之后是一个个journal_block_tag_t结构,该结构表示日志记录了文件系统中哪些block的副本,结构如下:
typedefstructjournal_block_tag_s
__u32t_blocknr;
/*Theon-diskblocknumber*/
__u32t_flags;
/*Seebelow*/
}journal_block_tag_t;
其中t_blocknr表示文件系统中的block号,t_flags标志为JFS_FLAG_LAST_TAG时,表示该descriptor所有blocktag结束。
3.2Commitblock
Commitblock只有一个journal_header_s结构表示。
3.3Revokeblock
Revokeblock的开头是一个journal_revoke_header_s结构,该结构之后跟随的是一个个4字节的整数,表示revoke的文件系统中的block号。
其中r_count表示在该block中有多少字节是有效数据。
typedefstructjournal_revoke_header_s
journal_header_tr_header;
intr_count;
/*Countofbytesusedintheblock*/
}journal_revoke_header_t;
4ext3写数据
功能:
在ext2写数据的流程上增加对日志的操作,把数据和元数据放入transaction相关的链表.需要注意的是,写数据其实并没有把数据或元数据写入日志(这是commit的工作),更没有把数据或元数据写入硬盘(这时checkpoint的工作),它只是把数据和元数据放入transaction相关的链表,以后会进一步处理。
4.1写数据流程图
4.2三种模式处理流程:
Journal
数据和元数据都是通过journal_dirty_metadata()函数放到t_buffer链表中去,先被写入日志,然后被放入commit_transaction->
t_checkpoint_list中去,在checkpoint的时候被写入硬盘
Ordered:
元数据处理过程同Journal。
数据被放到commit_transaction->
t_sync_datalist,在处理t_buffer链表之前被写入硬盘,也就是说在元数据写入日志之前数据被写入硬盘
Writeback:
数据不做任何特殊处理,直接暴露在内存中,标为脏,通过内核的VM层写入磁盘
可见,Writeback和Ordered模式的区别是ordered模式把数据放入了commit_transaction->
t_sync_datalist,在把元数据写入日志之前先把这些数据写入硬盘,从而保证了处理之间的先后关系,而Writeback没有,所以也就不会有这个保证。
4.3写数据流程详解
首先看函数调用关系:
ext3_file_write()
generic_file_aio_write
generic_file_aio_write_nolock
__generic_file_aio_write_nolock
generic_file_buffered_write
可以看出在generic_file_buffered_write函数以上,ext3与ext2函数的调用关系完全一致。
也就是说,ext3的写数据流程完全是构建在ext2上的,只是在需要的时候增加对日志相关的操作,下面我们来看增加了那些操作:
下面我们根据上面的流程图来详细解释下每步做了什么:
1)ext3_prepare_write()首先会计算出对一个page的修改最多会修改到多少个文件系统中的block
2)joural_start()中获取当前handle,并判断然后判断当前的journal->
j_running_transaction是否存在,如果不存在则创建一个
3)写入数据是会到blockbitmap、groupdescripor等元数据的改动,因此会调用ext3_journal_dirty_metadata()来处理相应的原数据,把元数据存储在当前transaction的t_buffer队列中。
需要注意的是,日志的三种模式都在这里把元数据存在这个链表中,之后会对数据采用不用的处理方法。
4)之后会对不用的日志模式对数据采取不同的处理方法:
a)journal模式:
调用ext3_journal_dirty_metadata()把数据也放入在当前transaction的t_buffer队列中,以后会对数据和元数据采用相同的操作
b)ordered模式:
调用ext3_journal_dirty_data()把数据放入当前transaction的t_sync_datalist中。
以后commit的时候会在处理元数据之前把这个链表的数据写入硬盘
c)数据不做任何特殊处理,直接暴露在内存中,标为脏,通过内核的VM层写入磁盘
4.4数据dirty位的转换过程(新增,针对数据)
4.4.1Journal模式:
4.4.1.1Preparewrite阶段:
ext3_prepare_write
|->
block_prepare_write
|->
__block_prepare_write遍历此次write操作所涉及的所有bh,查看uptodate情况,从磁盘映射需要涉及的块;
map失败,走下面灰色部分
mark_buffer_dirty把buffer_head标为dirty
_set_page_dirty_nobuffers标记所在page为dirty,
radix_tree_tag_set标志radixtree中对应page为dirty;
_mark_inode_dirty如果page是文件的,标志文件的inode为dirty
如果是jounaled模式:
walk_page_buffers:
遍历此次write操作所涉及的所有bh
do_journal_get_write_access用该函数处理每个bh
ext3_journal_get_write_access
__ext3_journal_get_write_access
journal_get_write_access
journal_add_journal_head创建一个jh,把其中的b_bh标志位指向bh,bh的b_private设置为本jh,设置bh的BH_JBD标志位,表示该bh已经被纳入jbd体系
do_get_write_access把每个bh数据块加入active事务的一个链表中,如果bh是脏的,那么有两种情况:
buffer是non-journaled,并且正在进行写回,什么也不做;
或者是journaled,如果该bh处于特定的队列中,调用jbd_unexpected_dirty_buffer;
jbd_unexpected_dirty_buffer:
清除bh的dirty位,置JBD_Dirty位
注:
符合条件的dirty的bh才会被置为jbddirty,条件是处于BJ_Metadata,BJ_Reserved,BJ_Shadow,BJ_Forget的任一队列中。
一般新的bh加入时没有处于任何队列中,因此不会被置为jbddirty。
4.4.1.2Commitwrite阶段:
ext3_journalled_commit_write
walk_page_buffers遍历此次write操作所涉及的所有bh(一个page中)
commit_write_fn对每个bh调用该函数
ext3_journal_dirty_metadata
journal_dirty_metadata
set_buffer_jbddirty(bh)置JBD_Dirty位
__journal_file_buffer把jh放入事务的BJ_Metadata队列中
ext3_mark_inode_dirty如果需要,设置inode为dirty?
?
4.4.2Ordered模式:
4.4.2.1Preparewrite阶段:
4.4.2.2Commitwrite阶段:
ext3_ordered_commit_write
walk_page_buffers()遍历此次write操作所涉及的所有bh
ext3_journal_dirty_data每个bh用该函数进行处理
journal_dirty_data不对dirty状态做任何调整,把jb放入BJ_SyncData队列;
如果jh已经在一个事务中并且状态为BJ_SyncData,立即刷回磁盘
journal_add_journal_head每个bh包装成一个jh,把其中的b_bh标志位指向bh,bh的b_private设置为本jh,设置bh的BH_JBD标志位,表示该bh已经被纳入jbd体系
generic_commit_write
__block_commit_write
set_buffer_uptodate(bh)&
mark_buffer_dirty(bh);
mark_inode_dirty如果需要,把inode置为dirty
4.4.3Writeback模式:
4.4.3.1Preparewrite阶段:
__block_prepare_write遍历此次write操作所涉及的所有bh
4.4.3.2Commitwrite阶段:
5写日志和写数据到硬盘
5.1Kjournal线程
这个是ext3的守护线程,每5s自我启动一次,主要负责两件事:
commit和checkpoint
5.1.1commit
改变当前transaction的state(不再是running了),然后根据不同的模式做不同的操作:
a)ordered模式:
把数据写到硬盘,把元数据写到日志,并提交到t_checkpoint_list
b)journalled模式:
把数据和元数据写到日志,并提交到t_checkpoint_list
触发条件:
a)由kjournal唤醒,kjournal线程本身5s会自启动一次,或者由一个wakeupevent启动
b)每次调用start_this_handle()的时候会检查当前transaction是否已经很大,使得话就commit
c)ext3_sync_fs()会启动commit
d)当要以readonly模式mount一个fs的时候,journal_flush()会先启动commit
e)由于出错或者其他原因导致而要destroy,abort或者restart当前journal的时候,会先commit
细节解析:
上面那些不同的触发条件最后都会调用__log_start_commit()来修改journal->
j_commit_request,然后唤醒kjournal线程调用journal_commit_transaction()函数来执行commit的
5.1.1.1数据结构以及其他
几个重要的list:
a)t_sync_datalist:
只保存ordered模式下数据.如果文件系统以"
ordered"
模式mount上,那么这个函数会把所有数据刷到硬盘上,并等到所有数据写完
b)t_buffers:
如果是journalled模式,数据和元数据都是存在这个list中的。
而在ordered模式下,这个list只保存元数据,因为journal_commit_transaction()里面先处理commit_transaction->
t_sync_datalist,并等待处理完毕,然后才处理commit_transaction->
t_buffers,这样就保证了ordered模式下数据被先写回磁盘,然后metadata才写入日志
c)t_checkpoint_list:
保存需要被写到磁盘上的数据。
t_buffers中的内容被复制成两份,一份被提交到日志,然后释放;
一份被提交到这个list里面,在checkpoint的时候写到硬盘
d)t_forget顾名思义,遗忘队列,当队列提交以后这个队列中的bh是不用cp的,所以在transaction生命中会把一些bh放到这个队列,然后结束之前会根据这些bh的状态来处理
commit过程中transaction的状态变化:
transaction->
t_state从T_RUNNING,T_FLUSH,T_COMMIT,之后会分两种情况,一种情况t_checkpoint_list为空,说明数据已经被写到硬盘上了,说明transaction的整个生命历程已经结束了,状态变为T_FINISHED;
另一种情况t_checkpoint_list不为空,当前commit_transaction就被挂到了journal->
j_checkpoint_transactions中去,等待checkpoint,然后数据都刷到硬盘上了,状态就变为T_FINISHED
t_buffer中的数据和元数据转换流程以及dirty状态:
5.1.1.2commit工作流程
commit的所有流程都集中在journal_commit_transaction()函数里面,具体流程如下:
1)锁定当前transactionand并等待所有的updates完成.比如前一个journal_flush对journalsb的影响,还有所有的commit_transaction->
t_updates
2)丢弃所有剩余的BJ_Reservedbuffers(t_reserved_list:
Doubly-linkedcircularlistofallbuffersreservedbutnotyetmodifiedbythistransaction)
3)清空journal->
j_checkpoint_transactions->
t_checkpoint_list,通过丢弃所有的written-backbuffers来释放一些内存
__journal_clean_checkpoint_list(journal);
4)切换到新的revoketable(j_revoke_table:
maintainsthelistofrevokedblocksinthecurrenttransaction),在当前jounal的两个revoketable中选择空余的那个,因为不想等到有内容的那个revoketable被写完。
然后会初始化这个revoketa
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ext3JBD 文档