8.5 日志设计

日志驻留在超级块中指定的已知固定位置。它由一个头块(header block)和一系列更新块的副本(logged block)组成。头块包含一个扇区号数组(每个logged block对应一个扇区号)以及日志块的计数。磁盘上的头块中的计数或者为零,表示日志中没有事务;或者为非零,表示日志包含一个完整的已提交事务,并具有指定数量的logged block。在事务提交(commit)时Xv6才向头块写入数据,在此之前不会写入,并在将logged blocks复制到文件系统后将计数设置为零。因此,事务中途崩溃将导致日志头块中的计数为零;提交后的崩溃将导致非零计数。

注:logged block表示已经记录了操作信息的日志块,而log block仅表示日志块

每个系统调用的代码都指示写入序列的起止,考虑到崩溃,写入序列必须具有原子性。为了允许不同进程并发执行文件系统操作,日志系统可以将多个系统调用的写入累积到一个事务中。因此,单个提交可能涉及多个完整系统调用的写入。为了避免在事务之间拆分系统调用,日志系统仅在没有文件系统调用进行时提交。

同时提交多个事务的想法称为组提交(group commit)。组提交减少了磁盘操作的数量,因为成本固定的一次提交分摊了多个操作。组提交还同时为磁盘系统提供更多并发写操作,可能允许磁盘在一个磁盘旋转时间内写入所有这些操作。Xv6的virtio驱动程序不支持这种批处理,但是Xv6的文件系统设计允许这样做。

Xv6在磁盘上留出固定的空间来保存日志。事务中系统调用写入的块总数必须可容纳于该空间。这导致两个后果:任何单个系统调用都不允许写入超过日志空间的不同块。这对于大多数系统调用来说都不是问题,但其中两个可能会写入许多块:writeunlink。一个大文件的write可以写入多个数据块和多个位图块以及一个inode块;unlink大文件可能会写入许多位图块和inode。Xv6的write系统调用将大的写入分解为适合日志的多个较小的写入,unlink不会导致此问题,因为实际上Xv6文件系统只使用一个位图块。日志空间有限的另一个后果是,除非确定系统调用的写入将可容纳于日志中剩余的空间,否则日志系统无法允许启动系统调用。