> For the complete documentation index, see [llms.txt](https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/lec16-file-system-performance-and-fast-crash-recovery-robert/16.7-file-system-hui-fu-guo-cheng.md).

# 16.7 ext3 file system恢复过程

为了简化重启时恢复软件的工作，当决定释放某段log空间时，文件系统会更新super block中的指针将其指向当前最早的transaction的起始位置。

![](/files/-MTJ5X2HtsC5vGfBZ7Nv)

之后如果crash并重启，恢复软件会读取super block，并找到log的起始位置。所以如果crash了，内存中的所有数据都会消失，例如文件系统中记录的哪些block被写入到了磁盘中这些信息都会丢失，所以可以假设这时内存中没有可用的数据，唯一可用的数据存在于磁盘中。当然我们这里的讨论都是基于磁盘还是完好的，所以你可以认为只是一次电力故障，系统突然停止了运行过程，在电力恢复时，断电那一瞬间磁盘中的数据还存在。我们并没有考虑磁盘被损坏或者被摧毁的情况。

crash或许会打断任何在进行中的transaction，或许transaction正在commit，或许transaction正在向文件系统写block。让我重新画一个例子，我们在log中有一个super block，之后是transaction T6，T7，T8，在T8之后是一个已近被释放了log空间的T5，假设T8已经用了T5的一部分空间。并且现在super block指向的是T6的起始位置，因为T6是最早的transaction。

![](/files/-MTJilfpRCtxg7hsBsSx)

现在crash并重启，恢复软件读取super block就可以知道log的起始位置，之后恢复软件会在log中一直扫描并尝试找到log的结束位置，现在我们需要有一种方式确定log的结束位置。我们知道每个transaction包含了一个descriptor block，里面记录了该transaction中包含了多少个data block，假设descriptor block记录了17个block，那么恢复软件会扫描17个data block，最后是commit block。这样可以一直扫描到T8。

![](/files/-MTJkniqq241MDCzCO-Z)

在扫描T8时有两种可能，一种可能是T8完成了commit，并且包含了commit block。这时恢复软件并不知道T8就是最后一个transaction，所以它会接着看T8的commit block的下一个block，来看看这是不是一个有效的descriptor block。我们知道这不是一个descriptor block，而是一个包含在T5内的随机block。现在的问题是恢复软件如何可靠的区分出来呢？是的，每个descriptor和commit block都以某个魔法数字作为起始，这是一个32bit的数字。所以如果扫描完了T8，下一个block以魔法数字作为起始，那么恢复软件就会认为这是一个descriptor block。（注，也有可能T5正好完美的跟在T8后面，也就是说T8的commit block之后就是T5的descriptor block，同时T5的commit block也存在，所以这里必然还需要一些其他的机制，我猜是用到了transaction的序列号）

但是，现在我们看到的block可能是包含了任意数据的data block，所以它可能是文件中的一个data block并且也是以魔法数字作为起始。所以这里的最后一个细节是，logging系统需要能区分一个以魔法数字作为起始的descriptor block和一个以魔法数字作为起始的data block。你可以想到各种方法来实现这种区分，ext3是这样做的，当它向log写一个block时，如果这个block既不是descriptor block也不是commit block，但是又以魔法数字作为起始，文件系统会以0替换前32bit，并在transaction的descriptor block中为该data block设置一个bit。这个bit表示，对应的data block本来是以魔法数字作为起始，但是现在我们将其替换成了0。而恢复软件会检查这个bit位，在将block写回到文件系统之前，会用魔法数字替换0。

因此，在log中，除了descriptor和commit block，不会有其他的block以这32bit的魔法数字作为起始。所以我们不会有模棱两可的判断，如果一个commit block之后的block以魔法数字作为起始，那么它必然是一个descriptor block。所以恢复软件会从super block指向的位置开始一直扫描，直到：

* 某个commit block之后的一个block并不是descriptor block
* 或者某个commit block之后是descriptor block，但是根据descriptor block找到的并不是一个commit block

这时，恢复软件会停止扫描，并认为最后一个有效的commit block是log的结束位置。或许在最后一个commit block之后会跟一个并没有commit完成的transaction（注，上面的第二种情况），但是恢复软件会忽略未完成的transaction，因为这个transaction并没有包含所有的写操作，所以它并不能原子性的恢复。之后恢复软件会回到log的最开始位置，并将每个log block写入到文件系统的实际位置，直到走到最后一个有效的commit block。之后才是启动剩下的操作系统，并且运行普通的程序。在恢复完成之前，是不能运行任何程序的，因为这个时候文件系统并不是有效的。

> 学生提问：XV6相比这里的log机制，缺少了什么呢？
>
> Robert教授：XV6主要缺失的是在log中包含多个transaction的能力，在XV6的log中最多只会有一个transaction，所以在XV6中缺少了并发的能力。比如说当我在执行transaction T7的系统调用时，ext3可以同时向磁盘提交T6，而这在XV6中这是不可能的，因为log只保存了一个transaction。所以我们必须先完成一个transaction的所有工作，之后才能开始下一个transaction。所以XV6是简单且正确的，但是缺少了并发的能力。
>
> 学生提问：但是在XV6我还是可以有多个transaction，只是说不能异步的执行它们，对吗？
>
> Robert教授：这里其实有点模糊，XV6实际上允许在一个transaction中包含多个系统调用（注，详见15.8），所以XV6有一些并发和batching的能力，但是当XV6决定要commit一个transaction时，在完全完成这个transaction之前，是不能执行任何新的系统调用的。因为直到前一个transaction完全完成，并没有log空间来存放新的系统调用。所以XV6要么是在运行一些系统调用，要么是在commit transaction，但是它不能同时干这两件事情，而ext3可以同时干这两件事情。
