参考:CSAPP 第8章
pleasewhy/xv6-book-2020-Chinese: MIT 6.S801 xv6 book 2020版 中文翻译 第4章
ECF(Exceptional Control Flow) 异常控制流
-
ECF是实现I/O、进程、虚拟内存的基本机制
-
应用程序通过ECF与操作系统进行交互,分为trap和system call系统调用,在xv6中被统称为trap。通过ECF来向操作系统请求服务。
-
理解ECF,就明白怎么调用C API,进而可以写各种各样的程序
-
ECF是操作系统中实现并发的基本机制,这种形式在简单的嵌入式程序中被称作中断。
-
ECF也可以实现应用程序的报错信息响应
总之,ECF是非常强大的系统功能。
1.异常
当CPU检测到异常出现的时候,无论是硬件的还是软件的,操作系统会根据既有的异常表exception table来跳转到各种异常处理程序exception handler。
异常分为四类
中断interrupt、陷阱trap、故障fault、终止abort
|--------|-----------|-----------|------------|------------------------|
| 类别 | 原因 | 异步/同步 | 返回行为 | 举例 |
| 中断 | 来自IO设备的信号 | 异步 | 总是返回到下一条指令 | reset按键 |
| 陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 | open系统调用 |
| 故障 | 潜在可恢复的错误 | 同步 | 可能会返回到当前指令 | page fault不一定是真的错误 |
| 终止 | 不可恢复的错误 | 同步 | 不会返回 | Data abort 非法指针访问(野指针) |
1.1中断
中断无时无刻都在发生,时钟中断非常频繁,键盘输入也是。中断恢复后,根本没有任何问题。完全是一种健康的机制。
1.2陷阱和系统调用
trap和syscall,用户经常要向操作系统请求服务,看起来和中断没什么区别。区别在于,其中cpu的状态发生了转变,从用户态到核心态再恢复到用户态。
Trap 是一种由软件指令有意触发的异常。它是 CPU 从用户模式(权限低)切换到内核模式(权限最高)的专用通道。Trap 也被称为软件中断 (Software Interrupt)。
system call系统调用是操作系统(OS)提供给应用程序的标准化接口。它是程序向操作系统请求服务的唯一合法途径。printf就是最常见的系统调用,通过write实现。
常见的系统调用任务:
-
文件操作:打开、读取、写入、关闭(open, read, write, close)。
-
进程控制:创建一个新进程、终止当前进程(fork, exit)。
-
网络通信:发送或接收网络数据包。
-
设备管理:请求使用摄像头、打印机等。
linux的系统调用可以参考:
https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/
1.3故障
由当前指令引起,但硬件认为还有救,操作系统尝试修复。修复成功则重新执行当前指令;修复失败则转为终止(Abort)。
1.4终止
程序或系统直接崩溃(蓝屏或段错误),直接被踢出运行队列。如硬件损坏、严重的内存访问冲突。
2.risc-v如何处理trap
2.1 名词解释
|---------------------|-------------------------------------------------------------------------|----------------------------------|-----------------------------------------------------------------------------|
| stvec | Supervisor Trap Vector,trap向量表 | ecall | Environment Call,环境调用指令 |
| sepc | Supervisor Exception Program Counter,异常计数器 | SIE | Supervisor Interrupt Enable,中断标志位 |
| scause | Supervisor Cause,记录trap原因 | MPP | Machine Previous Privilege,机器模式下SPP状态位 |
| sscratch | Supervisor Scratch,方便trap发生后恢复环境 | SPP | Supervisor Previous Privilege,记录Trap发生前,cpu的状态 |
| sstatus | Supervisor Status,记录trap时系统状态 | hartid | Hardware Thread ID,CPU id |
| sret | Supervisor Return,汇编指令 | trapframe | 陷阱帧,用来在 Trap 刚发生时,保存事故现场信息 |
| pc | 程序计数器,当前程序运行到内存的哪个地址了 | PTE | Page Table Entry,页表的最小单位 |
| satp | Supervisor Address Translation and Protection | TLB | Translation Lookaside Buffer,MMU的一级cache |
| trampoline | 跳板页,用户页表和内核页表映射到了完全相同的虚拟地址 | page-fault | 缺页异常,页表中找不到时发生的错误 |
| COW | Copy-On-Write, 写时复制 | uservec / usertrap / userret | uservec 是汇编入口,负责保存寄存器并切换页表;usertrap 是 C 语言,决定如何响应;userret 则是恢复环境、返回用户空间的出口。 |
| lazy allocation | 延迟分配, 应对程序贪婪要内存的策略。内核先在页表打个无效标记,等程序真正去访问这块内存(触发 page-fault)时,才动真格分配物理内存 | | |
2.2 寄存器功能
-
stvec:内核在这里写下 trap 处理程序的地址;RISC-V 到这里来处理 trap。
-
sepc :当 trap 发生时,RISC-V 会将程序计数器保存在这里(因为 PC 会被 stvec 覆盖)。sret (从 trap 中返回)指令将 sepc 复制到 pc 中。内核可以写 sepc 来控制 sret的返回到哪里。
-
scause:RISC -V 在这里放了一个数字,描述了 trap 的原因。
-
sscratch:内核在这里放置了一个值,这个值会方便 trap 恢复/储存用户上下文。
-
sstatus : sstatus 中的 SIE 位控制设备中断是否被启用,如果内核清除 SIE ,RISCV 将推迟设备中断,直到内核设置 SIE 。如果内核清除 SIE ,RISC-V 将推迟设备中断,直到内核设置 SIE 。SPP 位表示 trap 是来自用户模式还是监督者模式,并控制sret返回到什么模式。
2.3 trap处理流程
当需要执行 trap 时,RISC-V 硬件对所有的 trap 类型(除定时器中断外)进行以下操作:
-
如果该 trap 是设备中断,且 sstatus SIE位为 0,则不要执行以下任何操作。
-
通过清除 SIE 来禁用中断。
-
复制 pc 到 sepc
-
将当前模式(用户或监督者)保存在 sstatus 的 SPP位。
-
在 scause设置该次 trap 的原因。
-
将模式转换为监督者。
-
将 stvec复制到 pc。
-
执行新的 pc
2.4 举例
以下为write的一次完整系统调用。

解析trampoline.S文件,参见kernel.ld链接文件,可以知道物理内存地址开始于0x80000000, 接下来是.text段。这段相当于text段中特殊化了trampoline段

trampoline实际上的功能就是在用户态与内核态切换时,会发生用户虚拟页表 、内核虚拟页表的切换。trampoline是变化中的不变,这样才能保证内存不跑飞。它在两个表中都指向同一个物理地址。
-
uservec是用户态到内核态的切换,保存寄存器内容到内存(trapframe)
-
userret是内核态到用户态的切换,从内存(trapframe)恢复寄存器
以下为完整的write流程说明:

trampoline就像小孩蹦床一样,让操作系统在用户态和内核态之间安全的反复横跳。其他的系统调用都是类似的。