6.S081-5用户空间和内核空间的切换–Trap机制
备注:本节最好跟实验一起看6.S081 附加Lab1 用户执行系统调用的过程(Trap) (实验才是重点——介绍了trap的执行过程)
1. 什么是Trap
程序运行是完成用户空间和内核空间的切换。每当
-
程序执行系统调用
-
程序出现了类似page fault、运算时除以0的错误
-
一个设备触发了中断使得当前程序运行需要响应内核设备驱动
都会发生这样的切换 。用户空间和内核空间的切换,通常被称为trap。
本章将主要关心的是,执行系统调用时计算机的状态 – 可以用寄存器状态来判断,主要关心的寄存器有:
-
PC 程序计数器(Program Counter Register)
-
mode标志位(是supervisor mode 还是 user mode)
-
SATP(Supervisor Address Translation and Protection)寄存器,它包含了指向page table的物理内存地址
-
STVEC(Supervisor Trap Vector Base Address Register)寄存器,它指向了内核中处理trap的指令的起始地址。
-
SEPC(Supervisor Exception Program Counter)寄存器,在trap的过程中保存程序计数器的值。
-
SSRATCH(Supervisor Scratch Register)寄存器
Trap的时候,我们需要做什么?(当前处于user mode,现在需要执行系统调用)
- 保存32个用户寄存器
- 保存当前PC
- mode切换成supervisor
- SATP从user page table切换成kernel_pagetable
- Stack Frame和Stack Pointer都需要改变,因为需要一个stack来调用kernel的函数
- 跳入kernel
你可以在supervisor mode完成,但是不能在user mode完成的工作,或许并没有你想象的那么有特权。
在supervisor mode中,就像普通的用户代码一样,也需要通过page table来访问内存。如果一个虚拟地址并不在当前由SATP指向的page table中,又或者SATP指向的page table中PTE_U=1,那么supervisor mode不能使用那个地址。
2. Trap的执行流程
如上图👆所示
write() -> ECALL -> uservec()(Trampoline.s) -> usertrap() -> syscall() -> sys_write() -> syscall() -> usertrapret() -> usertrapret() ->ret
3. 具体的过程可以看
6.S081 附加Lab1 用户执行系统调用的过程(Trap)
附加:
学生提问:read和write系统调用,相比内存的读写,他们的代价都高的多,因为它们需要切换模式,并来回捣腾。有没有可能当你执行打开一个文件的系统调用时, 直接得到一个page table映射,而不是返回一个文件描述符?这样只需要向对应于设备的特定的地址写数据,程序就能通过page table访问特定的设备。你可以设置好限制,就像文件描述符只允许修改特定文件一样,这样就不用像系统调用一样在用户空间和内核空间来回捣腾了。 —— 感觉这个学生提问很有水平。
写数据,程序就能通过page table访问特定的设备。你可以设置好限制,就像文件描述符只允许修改特定文件一样,这样就不用像系统调用一样在用户空间和内核空间来回捣腾了。
Robert教授:这是个很好的想法。实际上很多操作系统都提供这种叫做内存映射文件(Memory-mapped file access)的机制,在这个机制里面通过page table,可以将用户空间的虚拟地址空间,对应到文件内容,这样你就可以通过内存地址直接读写文件。实际上,你们将在mmap 实验中完成这个机制。对于许多程序来说,这个机制的确会比直接调用read/write系统调用要快的多。