Skip to content

日志层

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

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

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

如果系统崩溃并重启,文件系统代码在运行任何进程之前会如下恢复:如果日志被标记为包含一个完整的操作,那么恢复代码会将写入复制到它们在磁盘文件系统中的所属位置。如果日志未被标记为包含一个完整的操作,恢复代码会忽略该日志。恢复代码最后会擦除日志。

为什么 xv6 的日志能解决文件系统操作期间崩溃的问题?如果崩溃发生在操作提交之前,那么磁盘上的日志将不会被标记为完成,恢复代码将忽略它,磁盘的状态将如同操作从未开始一样。如果崩溃发生在操作提交之后,那么恢复将重放操作的所有写入,如果操作已开始将它们写入磁盘上的数据结构,则可能会重复它们。在任何一种情况下,日志都使操作相对于崩溃是原子的:恢复后,操作的所有写入要么全部出现在磁盘上,要么全都不出现。