ext3JBD文档.docx
- 文档编号:29930008
- 上传时间:2023-08-03
- 格式:DOCX
- 页数:25
- 大小:429.41KB
ext3JBD文档.docx
《ext3JBD文档.docx》由会员分享,可在线阅读,更多相关《ext3JBD文档.docx(25页珍藏版)》请在冰豆网上搜索。
ext3JBD文档
Ext3文件系统
1Ext3文件系统简介
Ext3一种日记式文件系统。
日记文件系统会把系统对磁盘文件系统的更改第一一一记录在日记文件中,然后再更新到磁盘上。
在由某种原因(例如down机等)而致使文件系统显现不一致的情形下,能够通过重放(replay)日记文件来恢复文件系统的一致性。
Ext3是直接从Ext2文件系统进展过来的,采纳了Ext2文件系统的磁盘数据布局,实现了对Ext2的完全兼容。
依照写入日记的内容和数据刷新时刻的不同,Ext3可支持三个不同的日记格式:
Journal模式,ordered模式和writeback模式。
1.1Ext3日记模式
第一介绍元数据的概念,在Ext2和Ext3中,有六种元数据,别离是:
超级块,块组描述符,节点,间接块,数据位图。
可见,元数据记录了数据的改变。
Ext3既能够只对元数据做日记,也能够同时对文件数据块做日记。
具体来讲,Ext3提供以下三种日记模式:
日记(Journal)
文件系统所有数据和元数据的改变都记入日记。
这种模式减少了丢失每一个文件所作修改的机遇,可是它需要很多额外的磁盘访问。
例如,当一个新文件被创建时,它的所有数据块都必需复制一份作为日记记录。
这是最平安和最慢的Ext3日记模式。
预定(Ordered)
只有对文件系统元数据的改变才记入日记。
但是,Ext3文件系统把元数据和相关的数据块进行分组,以便把元数据写入磁盘之前写入数据块。
如此,就能够够减少文件内数据损坏的机遇;例如,确保增大文件的任何写访问都完全受日记的爱惜。
这是缺省的Ext3日记模式。
写回(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:
元数据处置进程同Journal。
数据不做任何特殊处置,直接暴露在内存中,标为脏,通过内核的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时期:
ext3_prepare_write
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时期:
ext3_prepare_write
|->block_prepare_write
|->__block_prepare_write遍历这次write操作所涉及的所有bh
4.4.3.2Commitwrite时期:
|->generic_commit_write
|->__block_commit_write
|->set_buffer_uptodate(bh)&mark_buffer_dirty(bh);
|->mark_inode_dirty若是需要,把inode置为dirty
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被写完。
然后会初始化那个revoketable,也确实是说,它此刻是空的
5)写revokeblock
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ext3JBD 文档