# 16.6 ext3 transaction commit步骤

基于上面的系统调用的结构，接下来我将介绍commit transaction完整的步骤。每隔5秒，文件系统都会commit当前的open transaction，下面是commit transaction涉及到的步骤：

1. 首先需要阻止新的系统调用。当我们正在commit一个transaction时，我们不会想要有新增的系统调用，我们只会想要包含已经开始了的系统调用，所以我们需要阻止新的系统调用。这实际上会损害性能，因为在这段时间内系统调用需要等待并且不能执行。
2. 第二，需要等待包含在transaction中的已经开始了的系统调用们结束。所以我们需要等待transaction中未完成的系统调用完成，这样transaction能够反映所有的写操作。
3. 一旦transaction中的所有系统调用都完成了，也就是完成了更新cache中的数据，那么就可以开始一个新的transaction，并且让在第一步中等待的系统调用继续执行。所以现在需要为后续的系统调用开始一个新的transaction。
4. 还记得ext3中的log包含了descriptor，data和commit block吗？现在我们知道了transaction中包含的所有的系统调用所修改的block，因为系统调用在调用get函数时都将handle作为参数传入，表明了block对应哪个transaction。接下来我们可以更新descriptor block，其中包含了所有在transaction中被修改了的block编号。
5. 我们还需要将被修改了的block，从缓存中写入到磁盘的log中。之前有同学问过，新的transaction可能会修改相同的block，所以在这个阶段，我们写入到磁盘log中的是transaction结束时，对于相关block cache的拷贝。所以这一阶段是将实际的block写入到log中。
6. 接下来，我们需要等待前两步中的写log结束。
7. 之后我们可以写入commit block。
8. 接下来我们需要等待写commit block结束。结束之后，从技术上来说，当前transaction已经到达了commit point，也就是说transaction中的写操作可以保证在面对crash并重启时还是可见的。如果crash发生在写commit block之前，那么transaction中的写操作在crash并重启时会丢失。
9. 接下来我们可以将transaction包含的block写入到文件系统中的实际位置。
10. 在第9步中的所有写操作完成之后，我们才能重用transaction对应的那部分log空间。

![](/files/-MTItADm7dtZxRttiN_A)

在一个非常繁忙的系统中，log的头指针一直追着尾指针在跑（注，也就是说一直没有新的log空间）。在当前最早的transaction的所有步骤都完成之前，或许不能开始commit一个新的transaction，因为我们需要重复利用最早的transaction对应的log空间。不过人们通常会将log设置的足够大，让这种情况就不太可能发生。

> 学生提问：你刚刚说没有进程会等待这些步骤完成，那么这些步骤是在哪里完成的呢？
>
> Robert教授：这些是在后台的内核线程完成的。
>
> 学生提问：我有个有关重用log空间的问题，假设我们使用了一段特定的log空间，并且这段log空间占据了是刚刚释放出来的所有log空间，但是还不够，那么文件系统会等待另一部分的log空间释放出来吗，还是会做点别的？
>
> Robert教授：是的，会等待。让我画张图来确保我回答的是正确的问题。我们可以认为log是磁盘中的一段线性空间，假设现存的transaction中最早的是T7，之后是T8，T9，我们想要将T10放在T9之后的空闲区域。

![](/files/-MTIz5KIB2hDx_rYvF1h)

> 我们或许要等待T7将所有的block写入到文件系统对应的位置，这样我们才能释放T7对应的空间。这意味着T10中的步骤需要暂停以等待T7释放出来。这是你的问题吗？
>
> 同一个学生：是的，所以可能是这样，我先写入T10的block到现有的log空闲区域，但是如果最后log足够大并且我们用光了空闲区域，我们就需要等待T7的空间被释放出来，是吗？
>
> Robert教授：是的，如果需要写入的数据足够多，并且log迅速的用光了。我们甚至都不能在释放log空间之前开始新的系统调用。如果你们关注细节的话，这里会有一些潜在的死锁。首先系统调用需要预声明需要多少个block，这样logging系统才知道对于该transaction需要多少log空间，因为我们不会在没有足够空间来commit transaction时，开始一个新的transaction（注，难道不能将不能写入到磁盘log中的transaction先缓存在内存中吗？虽然这样可能会导致堆积）。
>
> 学生提问：如果新的transaction需要的空间走到了T8，那么现在就需要等待T7，T8结束，这是怎么工作的呢？
>
> Robert教授：图中的T7，T8，T9其中的系统调用都完成了，并且都已经在commit到log中了。在上面的图中，我们会直接开始T10，新的系统调用会写入到transaction T10，最终当T10需要commit到log中，并且它大到需用用到T8的空间时，它需要等待T7，T8结束。文件系统会记录每个transaction的大小，这样文件系统就知道要等待多少个之前的transaction结束。所以这里还有不少的记录工作，这样文件系统才能理解所有旧的transaction的状态。

有关如何重用log空间，这里有个小细节。在log的最开始有一个super block，所以在任何时候log都是由一个super block和一些transaction组成。假设T4是最新的transaction，之前是T1，T2，T3。

![](/files/-MTJ3VEiXHqD0Yzer2i8)

我们是否能重用一段log空间，取决于相应的transaction，例如T2，是否已经commit并且写入到文件系统的实际位置中，这样在crash并重启时就不需要重新执行这段transaction了。同时也取决于T2之前的的所有transaction是否已经被释放了。所有的这些条件都满足时，我们就可以释放并重用T2对应的log空间。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/lec16-file-system-performance-and-fast-crash-recovery-robert/16.6-ext3-transaction-commit-bu-zhou.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
