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操作,要么一个都不执行。TransactionJBD将多个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数据结构关系tablejournal_superblock_ss_maxlens_blocksizes_headertableJournal_tj_superblocks_firstj_running_transactiontablejournal_header_th_sequenceh_blocktypeh_magicj_chckpoint_transactionsj_committing_transactionj_headj_tailj_freej_firstj_maxlenj_inodej_tail_sequencej_transaction_sequencej_commit_sequencej_commit_requestj_commit_timerj_revokej_wbuftabletransaction_tt_journalt_tidt_nr_bufferst_log_startt_reserved_listt_locked_listt_bufferst_sync_datalistt_forgett_checkpoint_listt_checkpoint_io_listt_iobuf_listt_shadow_listt_log_listt_cpnextt_cpprevtablejournal_headb_bhb_jcountb_transactionb_jlistb_next_transactionb_tnextb_tprevb_cp_transactionb_cpnextb_cpprevs_sequences_starts_max_transactiontablebuffer_headb_datab_blocknrb_page3日志文件格式日志文件.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_BLOCK53.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.2CommitblockCommitblock只有一个journal_header_s结构表示。3.3RevokeblockRevokeblock的开头是一个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写数据流程图generic_file_writeext3_prepare_writeext3_writepage_trans_blocksext3_journal_startblock_prepare_writejournal_startext3_get_blockext3_get_block_handleext3_alloc_blockext3_journal_get_create_accessext3_journal_dirty_metadatawalk_page_buffersJournal模式ext3_journal_forgetdo_journal_get_write_access三种模式分别对应:ext3_journalled_commit_write;ext3_ordered_commit_write;ext3_writeback_commit_write;ext3_journal_stopjournal_stopcommit_write