> 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/lec06-isolation-and-system-call-entry-exit-robert/6.2-trap-dai-ma-zhi-xing-liu-cheng.md).

# 6.2 Trap代码执行流程

我首先会简单介绍一下trap代码的执行流程，但是这节课大部分时间都会通过gdb来跟踪代码是如何通过trap进入到内核空间，这里会涉及到很多的细节。为了帮助你提前了解接下来的内容，我们会跟踪如何在Shell中调用write系统调用。从Shell的角度来说，这就是个Shell代码中的C函数调用，但是实际上，write通过执行ECALL指令来执行系统调用。ECALL指令会切换到具有supervisor mode的内核中。在这个过程中，内核中执行的第一个指令是一个由汇编语言写的函数，叫做uservec。这个函数是内核代码trampoline.s文件的一部分。所以执行的第一个代码就是这个uservec汇编函数。

![](/files/-MKAdskQx9FFg_U2vJxE)

之后，在这个汇编函数中，代码执行跳转到了由C语言实现的函数usertrap中，这个函数在trap.c中。

![](/files/-MKFmXx4s73WknveUbxP)

现在代码运行在C中，所以代码更加容易理解。在usertrap这个C函数中，我们执行了一个叫做syscall的函数。

![](/files/-MKFmzhqvIsysa8emSuU)

这个函数会在一个表单中，根据传入的代表系统调用的数字进行查找，并在内核中执行具体实现了系统调用功能的函数。对于我们来说，这个函数就是sys\_write。

![](/files/-MKFoWvOeYinacZ3vtpn)

sys\_write会将要显示数据输出到console上，当它完成了之后，它会返回给syscall函数。

![](/files/-MKFpmF6vC4PIFCMOeAh)

因为我们现在相当于在ECALL之后中断了用户代码的执行，为了用户空间的代码恢复执行，需要做一系列的事情。在syscall函数中，会调用一个函数叫做usertrapret，它也位于trap.c中，这个函数完成了部分方便在C代码中实现的返回到用户空间的工作。

![](/files/-MKFqp6tuFh6n7Ga5HH_)

除此之外，最终还有一些工作只能在汇编语言中完成。这部分工作通过汇编语言实现，并且存在于trampoline.s文件中的userret函数中。

![](/files/-MKHxWqlr_axel0YbT3l)

最终，在这个汇编函数中会调用机器指令返回到用户空间，并且恢复ECALL之后的用户程序的执行。

![](/files/-MKHxleUqYy-y0mrS48w)

对于这里的概述大家有问题吗？没有的话我要切到gdb了。

> 学生提问：vm.c运行在什么mode下？
>
> Robert教授：vm.c中的所有函数都是内核的一部分，所以运行在supervisor mode。
>
> 学生提问：为什么这些函数叫这些名字？
>
> Robert教授：现在的函数命名比较乱，明年我会让它们变得更加合理一些。（助教说）我认为命名与寄存器的名字有关。
>
> 学生提问：难道vm.c里的函数不是要直接访问物理内存吗？
>
> Robert教授：是的，这些函数能这么做的原因是，内核小心的在page table中设置好了各个PTE。这样当内核收到了一个读写虚拟内存地址的请求，会通过kernel page table将这个虚拟内存地址翻译成与之等价物理内存地址，再完成读写。所以，一旦使用了kernel page table，就可以非常方便的在内核中使用所有这些直接的映射关系。但是直到trap机制切换到内核之前，这些映射关系都不可用。直到trap机制将程序运行切换到内核空间之前，我们使用的仍然是没有这些方便映射关系的user page table。
>
> 学生提问：这个问题或许并不完全相关，read和write系统调用，相比内存的读写，他们的代价都高的多，因为它们需要切换模式，并来回捣腾。有没有可能当你执行打开一个文件的系统调用时， 直接得到一个page table映射，而不是返回一个文件描述符？这样只需要向对应于设备的特定的地址写数据，程序就能通过page table访问特定的设备。你可以设置好限制，就像文件描述符只允许修改特定文件一样，这样就不用像系统调用一样在用户空间和内核空间来回捣腾了。
>
> Robert教授：这是个很好的想法。实际上很多操作系统都提供这种叫做内存映射文件（Memory-mapped file access）的机制，在这个机制里面通过page table，可以将用户空间的虚拟地址空间，对应到文件内容，这样你就可以通过内存地址直接读写文件。实际上，你们将在mmap 实验中完成这个机制。对于许多程序来说，这个机制的确会比直接调用read/write系统调用要快的多。
