内容发布更新时间 : 2024/11/8 21:30:42星期一 下面是文章的全部内容请认真阅读。
Ext3文件系统
1 Ext3文件系统简介
Ext3一种日志式文件系统。日志文件系统会把系统对磁盘文件系统的更改首先一一记录在日志文件中,然后再更新到磁盘上。在由某种原因(例如down机等)而导致文件系统出现不一致的情况下,可以通过重放(replay)日志文件来恢复文件系统的一致性。
Ext3是直接从Ext2文件系统发展过来的,采用了Ext2文件系统的磁盘数据布局,实现了对Ext2的完全兼容。
根据写入日志的内容和数据刷新时间的不同,Ext3可支持三个不同的日志格式:Journal模式,ordered模式和writeback模式。
1.1 Ext3日志模式
首先介绍元数据的概念,在Ext2 和 Ext3中,有六种元数据,分别是:超级块,块组描述符,节点,间接块,数据位图。可见,元数据记录了数据的改变。
Ext3既可以只对元数据做日志,也可以同时对文件数据块做日志。具体来说,Ext3提供以下三种日志模式:
日志(Journal )
文件系统所有数据和元数据的改变都记入日志。这种模式减少了丢失每个文件所作修改的机会,但是它需要很多额外的磁盘访问。例如,当一个新文件被创建时,它的所有数据块都必须复制一份作为日志记录。这是最安全和最慢的Ext3日志模式。
预定(Ordered )
只有对文件系统元数据的改变才记入日志。然而,Ext3文件系统把元数据和相关的数据块进行分组,以便把元数据写入磁盘之前写入数据块。这样,就可以减少文件内数据损坏的机会;例如,确保增大文件的任何写访问都完全受日志的保护。这是缺省的Ext3 日志模式。
写回(Writeback )
只有对文件系统元数据的改变才记入日志;这是在其他日志文件系统发现的方法,也是最快的模式
1.2 日志块设备(JBD)
Ext3 文件系统本身不处理日志,而是利用日志块设备(Journaling Block Device)或叫JBD 的通用内核层。Ext3文件系统调用JDB例程以确保在系统万一出现故障时它的后续操作不会损坏磁盘数据结构。Ext3 与JDB 之间的交互本质上基于三个基本单元:日志记录,原子操作和事务。
日志记录本质上是文件系统将要发出的低级操作的描述。在某些日志文件系统中,日志记录只包括操作所修改的字节范围及字节在文件系统中的起始位置。然而,JDB 层使用的日
志记录由低级操作所修改的整个缓冲区组成。这种方式可能浪费很多日志空间(例如,当低级操作仅仅改变位图的一个位时),但是,它还是相当快的,因为JBD 层直接对缓冲区和缓冲区首部进行操作。
修改文件系统的任一系统调用都通常划分为操纵磁盘数据结构的一系列低级操作。如果这些低级操作还没有全部完成系统就意外宕机,就会损坏磁盘数据。为了防止数据损坏,Ext3文件系统必须确保每个系统调用以原子的方式进行处理。原子操作是对磁盘数据结构的一组低级操作,这组低级操作对应一个单独的高级操作。
出于效率的原因,JBD 层对日志的处理采用分组的方法,即把属于几个原子操作处理的日志记录分组放在一个单独的事务中。此外,与一个处理相关的所有日志记录都必须包含在同一个事务中。一个事务的所有日志记录都存放在日志的连续块中。JBD层把每个事务作为整体来处理。例如,只有当包含在一个事务的日志记录中的所有数据提交给文件系统时才回收该事务所使用的块。
2 JBD层
2.1 JBD层基本概念
Log record
记录文件系统中一个block的改动。记录格式为journal_block_tag_t结构,存储在日志文件.journal中的discriptor block。当JBD层把一个block写入磁盘时,给block对应的buffer head添加一个journal_head结构。
Atomic operation handle
一个system call对应的所有log record组织在一起,称为一个handle。为保证文件系统一致性,handle为原子操作,当恢复系统fsck时,要么执行一个handle中的所有log record操作,要么一个都不执行。
Transaction
JBD将多个handle组织成一个transaction,transaction中的所有log record存储在日志文件的连续的块中。当一个transaction中的所有log record被提交到磁盘上之后,这个transaction占用的block就可以被回收再利用了。
transaction的整个生命历程中的状态和完成的工作如下:
1) T_RUNNING:表示当前transaction正在运行,可以接收数据到各个链表。每当发起一个写数据操作的时候,就会启动一个new handle, new handle会检查当前是否有running transaction,如果有,就向这个transaction里面提交数据。如果当前没有running transaction,就会创建一个新的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的 checkpoint list,在合适的时候把数据或元数据写入硬盘。可见,checkpoint 就是把数据或元数据写入硬盘。 6) T_FINISHED:这时数据已经写入硬盘,transaction工作完毕,可以从日志中删除了
2.2 Jbd数据结构关系
journal_superblock_ss_headerJournal_ts_blocksizes_maxlenj_superblockj_running_transactionj_committing_transactionj_chckpoint_transactionsj_headj_tailj_freej_firstj_maxlentablej_inodej_tail_sequencej_transaction_sequencej_commit_sequencej_commit_requestj_commit_timerj_revokej_wbufs_firsttables_sequences_starts_max_transactionjournal_header_th_magictableh_blocktypeh_sequencetransaction_tjournal_headt_journalt_tidt_log_startt_nr_bufferst_reserved_listt_locked_listt_bufferst_sync_datalisttablet_forgett_checkpoint_listt_checkpoint_io_listt_iobuf_listt_shadow_listt_log_listb_paget_cpnextt_cpprevtableb_blocknrb_databuffer_headb_bhb_jcountb_jlistb_transactionb_next_transtableactionb_tnextb_tprevb_cp_transactionb_cpnextb_cpprev