8.4 日志层

文件系统设计中最有趣的问题之一是崩溃恢复。出现此问题的原因是,许多文件系统操作都涉及到对磁盘的多次写入,并且在完成写操作的部分子集后崩溃可能会使磁盘上的文件系统处于不一致的状态。例如,假设在文件截断(将文件长度设置为零并释放其内容块)期间发生崩溃。根据磁盘写入的顺序,崩溃可能会留下对标记为空闲的内容块的引用的inode,也可能留下已分配但未引用的内容块。

后者相对来说是良性的,但引用已释放块的inode在重新启动后可能会导致严重问题。重新启动后,内核可能会将该块分配给另一个文件,现在我们有两个不同的文件无意中指向同一块。如果xv6支持多个用户,这种情况可能是一个安全问题,因为旧文件的所有者将能够读取和写入新文件中的块,而新文件的所有者是另一个用户。

Xv6通过简单的日志记录形式解决了文件系统操作期间的崩溃问题。xv6系统调用不会直接写入磁盘上的文件系统数据结构。相反,它会在磁盘上的log(日志)中放置它希望进行的所有磁盘写入的描述。一旦系统调用记录了它的所有写入操作,它就会向磁盘写入一条特殊的commit(提交)记录,表明日志包含一个完整的操作。此时,系统调用将写操作复制到磁盘上的文件系统数据结构。完成这些写入后,系统调用将擦除磁盘上的日志。

如果系统崩溃并重新启动,则在运行任何进程之前,文件系统代码将按如下方式从崩溃中恢复。如果日志标记为包含完整操作,则恢复代码会将写操作复制到磁盘文件系统中它们所属的位置。如果日志没有标记为包含完整操作,则恢复代码将忽略该日志。恢复代码通过擦除日志完成。

为什么xv6的日志解决了文件系统操作期间的崩溃问题?如果崩溃发生在操作提交之前,那么磁盘上的登录将不会被标记为已完成,恢复代码将忽略它,并且磁盘的状态将如同操作尚未启动一样。如果崩溃发生在操作提交之后,则恢复将重播操作的所有写入操作,如果操作已开始将它们写入磁盘数据结构,则可能会重复这些操作。在任何一种情况下,日志都会使操作在崩溃时成为原子操作:恢复后,要么操作的所有写入都显示在磁盘上,要么都不显示。