原文链接:
文件系统要解决的一个关键问题是如何避免掉电或系统崩溃导致数据受损,在这种意外风波中,造成文件系统受损的根本缘由在于写文件不是原子操作,由于写文件涉及的不仅仅是用户数据,还涉及元数据(metadata)包括Superblock、inodebitmap、inode、datablockbitmap等,所以写操作难以一步完成,假如其中任何一个步骤被打断,都会导致数据的不一致或毁坏。
举一个简化的事例,我们对一个文件进行写操作,要涉及以下步骤:
1、从datablockbitmap中分配一个数据块;
2、在inode中添加指向数据块的表针;
3、把用户数据写入数据块。
日志文件系统(JournalFileSystem)就是为解决上述问题而诞生的。
它的原理是在进行写操作之前,把正式进行的各个步骤(称为transaction)事先记录出来,保存在文件系统上单独开辟的一块空间上,这就是所谓的日志(journal),也被称为write-aheadlogging,日志保存成功以后才进行真正的写操作、把文件系统的元数据和用户数据写进硬碟(称为checkpoint),这样万一写操作的过程中掉电,上次挂载文件系统之前把保存好的日志重新执行一遍就行了(术语称作replay),防止了前述的数据损毁场景。
有人问若果保存日志的过程中掉电如何办?最初始的看法是把一条日志的数据一次性写入硬碟,相当于一个原子操作linux 系统重启日志,但是这并不可行linux 下载,由于硬碟一般以512字节为单位进行操作,日志数据一超过512字节就不可能一次性写入了。所以实际上是如此做的:给每一条日志设置一个结束符,只有在日志写入成功以后才写结束符,假如一条日志没有对应的结束符才会被视为无效日志,直接扔掉,这样就保证了日志里的数据是完整的。
一条日志在它对应的写操作完成以后就没用了,占用的硬碟空间就可以释放。保存日志的硬碟空间大小是有限的,被循环使用,所以日志也被称为circularlog。
至此可以总结一下日志文件系统的工作步骤了:
以上方法把用户数据(userdata)也记录在日志中,称为DataJournaling,LinuxEXT3文件系统就支持这些方法linux教程,这些方法存在效率问题:
就是每一个写操作涉及的元数据(metadata)和用户数据(userdata)实际上都要在硬碟上写两次,一次写在日志里,一次写在文件系统上。元数据倒也罢了,用户数据一般比较大linux 系统重启日志,拷贝几个GB的影片文件也要减去2实在是增加了效率。
一个更高效的方法是MetadataJournaling,不把用户数据(userdata)记录在日志中,它避免数据受损的方式是先写入用户数据(userdata)、再写日志,即在上述”Journalwrite”之前先写用户数据,这样就保证了只要日志是有效的,这么它对应的用户数据也是有效的,一旦发生掉电故障,最坏的结果也就是最后一条日志没记完,这么对应的用户数据也会丢,疗效与DataJournaling遗弃日志一样,重要的是文件系统的一致性和完整性是有保证的。
MetadataJournaling又叫OrderedJournaling,大多数文件系统都采用这些方法。像LinuxEXT3文件系统也是可以选择DataJournaling还是OrderedJournaling的。
参考资料:CrashConsistency:FSCKandJournaling