# 16.3 ext3 file system log format

ext3文件系统就是基于今天要阅读的[论文](https://pdos.csail.mit.edu/6.828/2020/readings/journal-design.pdf)，再加上几年的开发得到的，并且ext3也曾经广泛的应用过。ext3是针对之前一种的文件系统（ext2）logging方案的修改，所以ext3就是在几乎不改变之前的ext2文件系统的前提下，在其上增加一层logging系统。所以某种程度来说，logging是一个容易升级的模块。

ext3的数据结构与XV6是类似的。在内存中，存在block cache，这是一种write-back cache（注，区别于write-through cache，指的是cache稍后才会同步到真正的后端）。block cache中缓存了一些block，其中的一些是干净的数据，因为它们与磁盘上的数据是一致的；其他一些是脏数据，因为从磁盘读出来之后被修改过；有一些被固定在cache中，基于前面介绍的write-ahead rule和freeing rule，不被允许写回到磁盘中。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT3OCM7LxI_e3yHrCXg%2F-MT6-hbLtFkwJyArkaM8%2Fimage.png?alt=media\&token=19f92d85-5d0a-4141-813f-3ea785193568)

除此之外，ext3还维护了一些transaction信息。它可以维护多个在不同阶段的transaction的信息。每个transaction的信息包含有：

* 一个序列号
* 一系列该transaction修改的block编号。这些block编号指向的是在cache中的block，因为任何修改最初都是在cache中完成。
* 以及一系列的handle，handle对应了系统调用，并且这些系统调用是transaction的一部分，会读写cache中的block

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT3OCM7LxI_e3yHrCXg%2F-MT61jpVdnXzdf3NUIMg%2Fimage.png?alt=media\&token=a44bc6ea-c186-4c04-9398-b4ee5fb5df80)

在磁盘上，与XV6一样：

* 会有一个文件系统树，包含了inode，目录，文件等等
* 会有bitmap block来表明每个data block是被分配的还是空闲的
* 在磁盘的一个指定区域，会保存log

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT3OCM7LxI_e3yHrCXg%2F-MT62MwGi6_PMAs9tKGt%2Fimage.png?alt=media\&token=f76be1bc-ef82-47fc-8add-340e4e76b233)

目前为止，这与XV6非常相似。主要的区别在于ext3可以同时跟踪多个在不同执行阶段的transaction。

接下来我们详细看一下ext3的log中有什么，这与XV6中的log有点不一样。在log的最开始，是super block。这是log的super block，而不是文件系统的super block。log的super block包含了log中第一个有效的transaction的起始位置和序列号。起始位置就是磁盘上log分区的block编号，序列号就是前面提到的每个transaction都有的序列号。log是磁盘上一段固定大小的连续的block。log中，除了super block以外的block存储了transaction。每个transaction在log中包含了：

* 一个descriptor block，其中包含了log数据对应的实际block编号，这与XV6中的header block很像。
* 之后是针对每一个block编号的更新数据。
* 最后当一个transaction完成并commit了，会有一个commit block

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT3OCM7LxI_e3yHrCXg%2F-MT65R6eIdHZpXO6ZZ8S%2Fimage.png?alt=media\&token=755ffa58-48f6-4784-a94d-cfa9c86aa7ca)

因为log中可能有多个transaction，commit block之后可能会跟着下一个transaction的descriptor block，data block和commit block。所以log可能会很长并包含多个transaction。我们可以认为super block中的起始位置和序列号属于最早的，排名最靠前的，并且是有效的transaction。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT66ObFz9yro09pElsP%2F-MT8GvWqF04nL5dPmHhj%2Fimage.png?alt=media\&token=a044312e-b866-4d72-9aec-87c08e2c87b0)

这里有一些细节对于后面的内容很重要。在crash之后的恢复过程会扫描log，为了将descriptor block和commit block与data block区分开，descriptor block和commit block会以一个32bit的魔法数字作为起始。这个魔法数字不太可能出现在数据中，并且可以帮助恢复软件区分不同的block。

> 学生提问：有没有可能使用一个descriptor block管理两个transaction？是不是只能一个transaction结束了才能开始下一个transaction？
>
> Robert教授：Log中会有多个transaction，但是的确一个时间只有一个正在进行的transaction。上面的图片没能很好的说明这一点，当前正在进行的transaction对应的是正在执行写操作的系统调用。所以当前正在进行的transaction只存在于内存中，对应的系统调用只会更新cache中的block，也就是内存中的文件系统block。当ext3决定结束当前正在进行的transaction，它会做两件事情：首先开始一个新的transaction，这将会是下一个transaction；其次将刚刚完成的transaction写入到磁盘中，这可能要花一点时间。所以完整的故事是，磁盘上的log分区有一系列旧的transaction，这些transaction已经commit了，除此之外，还有一个位于内存的正在进行的transaction。在磁盘上的transaction，只能以log记录的形式存在，并且还没有写到对应的文件系统block中。logging系统在后台会从最早的transaction开始，将transaction中的data block写入到对应的文件系统中。当整个transaction的data block都写完了，之后logging系统才能释放并重用log中的空间。所以log其实是个循环的数据结构，如果用到了log的最后，logging系统会从log的最开始位置重新使用。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MT66ObFz9yro09pElsP%2F-MT8RrSo50nbdPOHcFmQ%2Fimage.png?alt=media\&token=fd56ea23-e681-4128-877c-a8ed7e74c865)

记住这里的log的结构，它对于后面的内容也很重要。
