文件系统不一致是如何产生的?
一个典型的不一致案例是向文件追加数据时掉电造成的孤儿block。正常情况下,所有block要么被某个文件的inode表记录,要么被free block list记录。如果不被前述任何之一记录,则为孤儿block。
假设文件系统在追加数据时的顺序是这样:
step1, 从free block list中分配block
step2, 往block中写数据
step3, 在文件的inode表中添加该block
如果凑巧在step3落实到介质之前发生了掉电,首先写入的内容丢失了,更重要的是,在step1中分配的block脱离管理了(在free block list和文件的inode表中都没有),成了孤儿block。内容丢失了可能被使用者检查出来;block脱管后,如果没有措施回收进free block list,这个block将再也没法使用。如果这样的事故不断累积,介质上的可用空间就会越来越少。
文件系统恢复要解决的问题
a.识别文件系统中不一致的地方并在恢复时消除不一致。
b.最小化恢复时间
c.最大化恢复数据
文件系统恢复的本质不是去恢复出已经丢失的信息,信息已经丢失了,不可能无中生有。恢复的目的在于损坏发生后,能够回退到最近的一个一致状态。
像所有的检错/纠错设计一样,文件系统的错误检测依赖于适当的冗余信息。这个和通信编码中的校验和设计是类似的,取决于冗余信息的多寡,还可以在检测错误的基础上进行适当程度的纠错。
恢复的前提是操作和数据的原子化,原子化以后,恢复就可以以原子的颗粒度进行,而不是一损俱损。数据修改原子化以后,系统的一致状态就能离散化,这就是所谓的checkpoints。数据的颗粒度是block,不同的文件系统叫法略有不同;至于操作的颗粒度,以追加数据为例,可能的划分为:分配block,写入数据,在文件inode list中添加该block。
传统文件系统(fat,ext2)如何处理恢复问题?
没有查到具体的资料,根据恢复所需要的时间(fsck on linux, scandisk on windows),应该是这样的:
系统重新mount文件系统时,如果发现上一次是非正常停机(可以在每次正常停机的时候设置标志位),则逐个检查介质上的每一个inode(block),确认其是否存在于free block list或者文件的inode list中,如果是孤儿,则回收(lost_found)。
可以看出,这种算法所花时间和inode的数量成正比,scalability差。
日志(journal)文件系统如何处理恢复问题:
日志文件系统解决了传统文件系统恢复时间太长的问题。其主要措施是在传统文件系统的基础上,另开辟一个日志文件来集中记录对文件系统的修改,当损坏发生时,可以以日志文件中最近的记录为标准,去核对文件系统的一致性,从而避免了传统文件系统恢复时逐个block的“地毯式”检查。具体的做法比如:重新mount文件系统的时候,如果发现上一次是非正常停机,则根据日志文件中的记录,检查介质上的对应block(inode)是否和日志中的记录一致,如果不一致,比如日志文件中记录某block已经被添加到某文件的inode list中,但是该文件的inode list中却没有该inode,说明往inode list中追加该block失败,简单的处理办法可以是把该block重新回收进free inode list。
当然,使用日志文件系统是,日志文件自己的一致性如何保证也是需要考虑的。
前面没有讨论如何最大化恢复数据,这个基本上取决于flush cache to disk的策略,cache越小,flush越频繁,丢失的数据就越少。
上面只讨论了block丢失的恢复,实际中,文件的毁坏还有其他情形,比如对文件已分配的block的改写丢失,从文件中删除的block的丢失。道理应该大同小异。
实际应用中,除了文件系统这个层面的一致性,应用程序本身也有自己的一致性要求,比如最典型的就是关系数据库管理系统,文件系统的一致是不够的,至少还需要记录层面的一致。
此外,设计可恢复的文件系统的一个基本出发点是,掉电等灾难导致的数据丢失是绝对的(除了block可能不得不回收到之前的状态,日志文件本身也有可能还来不及更新到介质,停机就发生了),我们能做的是维护文件系统的一致性。要提高存储的相对可靠性,必须借助设备冗余和紧急电源机制等方法。
文章评论