Skip to content

定时器中断

Xv6 使用定时器中断来维护其当前时间的概念,并在计算密集型进程之间进行切换。定时器中断来自连接到每个 RISC-V CPU 的时钟硬件。Xv6 对每个 CPU 的时钟硬件进行编程,以使其定期中断 CPU。

start.c 中的代码设置了一些控制位,允许在 supervisor 模式下访问定时器控制寄存器,然后请求第一个定时器中断。time 控制寄存器包含一个硬件以稳定速率递增的计数;这作为当前时间的概念。stimecmp 寄存器包含 CPU 将引发定时器中断的时间;将 stimecmp 设置为 time 的当前值加上 x 将在未来 x 个时间单位后安排一个中断。对于 qemu 的 RISC-V 仿真,1000000 个时间单位大约是十分之一秒。

定时器中断像其他设备中断一样,通过 usertrapkerneltrap 以及 devintr 到达。定时器中断到达时,scause 的低位设置为 5;trap.c 中的 devintr 检测到这种情况并调用 clockintr。后一个函数递增 ticks,允许内核跟踪时间的流逝。递增只在一个 CPU 上发生,以避免在有多个 CPU 时时间过得更快。clockintr 唤醒任何在 sleep 系统调用中等待的进程,并通过写入 stimecmp 来安排下一个定时器中断。

devintr 对定时器中断返回 2,以向 kerneltrapusertrap 指示它们应该调用 yield,以便 CPU 可以在可运行的进程之间进行多路复用。

内核代码可能被定时器中断中断,该中断通过 yield 强制进行上下文切换,这是 usertrap 中的早期代码在启用中断之前小心保存诸如 sepc 之类的状态的部分原因。这些上下文切换也意味着内核代码的编写必须意识到它可能会在没有警告的情况下从一个 CPU 移动到另一个 CPU。