The Sejolyn Memo

Back

每个 RISC-V CPU 都有一组特权寄存器,内核写入这些控制寄存器来告诉 CPU 如何处理 trap,并且内核可以读取这些寄存器来找出已发生的 trap(在 kernel/riscv.h 中定义)。

重要特权寄存器概述:

  • stevctrap handler 的地址,由内核写入,告诉
  • sepc:保存 trap 发生时的程序寄存器(因为 pc 随后会被 stvec 中的值覆盖)。sret(从 trap 返回)指令将 sepc 复制到 pc,可以通过编写 sepc 来控制 sret 的去向
  • scause:放置一个数字来描述 trap 的原因
  • sscratch:放在 trap handler 的最开始处,防止在保存用户寄存器之前覆盖它们
  • sstatus:其中的 SIE 位控制是否启用设备中断。如果内核清除 SIE,RISC-V 将推迟设备中断,直到内核设置 SIE。SPP 位指示 trap 是来自用户模式还是管理模式,并控制 sret 返回的模式
  • satp当前页表的根地址 上述寄存器与内核模式下处理的 trap 相关,并且不能在用户模式下读取或写入

RISC-V 硬件会对所有 trap 类型(定时器中断除外)执行以下操作:

  1. 中断屏蔽检查
    • 如果 trap 是由设备中断引发的,且 sstatus.SIE=0,则处理器会暂存该中断,暂缓执行
    • 如果是异常系统调用,会跳过该步骤
  2. 禁用中断
    • 设置 sstatus.SIE=0:防止 trap 处理期间被其他中断嵌套
  3. 保存上下文
    • sepc:保存当前 pc,以便 trap 返回时恢复执行
    • sstatus.SPP:保存当前特权模式(0=user, 1=kernel)
  4. 设置 trap 原因
    • scause:记录 trap 类型(中断或异常)和具体原因(如中断号或异常码)。
  5. 切换管理模式
    • 将当前模式设置为 Supervisor Mode,以便执行内核中的 trap handler。
  6. 跳转到 trap handler
    • 将 stvec 的地址加载到 pc 。 以上步骤为硬件操作,在发生 trap 后自动执行,并没有显式代码。

中断屏蔽检查中为什么区别对待?

  1. 设备中断的异步性
    • 设备中断是异步的,可以在任何时候发生。因此操作系统需要暂时屏蔽中断,以确保某些关键代码不被中断干扰。
    • 屏蔽中断时,新的中断可以被暂存,等到中断重启时再处理。
  2. 异常和系统调用的同步性
    • 异常和系统调用是由当前指令直接触发的,是同步事件。
    • 异常通常表示必须立即处理的错误或特殊情况,如果不处理,程序无法正确执行。
    • 系统调用是程序主动请求内核服务,如果不处理,程序会一直等待。

未完成的步骤(需软件处理):

  • 切换页表:CPU 不会自动切换页表
  • 切换栈指针:CPU 不会自动切换栈
  • 保存寄存器:通用寄存器需由软件保存 CPU 保留上述步骤交给软件处理是为软件提供灵活性,比如某些操作系统在某些情况下会省略页表切换,硬件仅提供最小必要的支持。而这些步骤都将在 trapline 页(uservec)中执行。
XV6 Trap机制(1):硬件支持
https://sejolyn.fyi/blog/6s081/trap%E6%9C%BA%E5%88%B61%E7%A1%AC%E4%BB%B6%E6%94%AF%E6%8C%81
Author Sejolyn
Published at August 18, 2025
Comment seems to stuck. Try to refresh?✨