2021-07-19

讨论记录 2021.07.19 #

参与成员:向勇、陈渝、贺鲲鹏、尤予阳

关于 CLIC、PLIC 和 CLINT #

目前 RISC-V 中断控制器的主流设计是,外部中断由 PLIC 负责,M 态的时钟中断和跨核软件中断由 CLINT 负责,二者相对独立。CLIC 希望提供低延迟、可抢占的中断系统,在小型单核系统上可以只有一个 CLIC,而多核系统的 PLIC 可以作为 CLIC 的一个中断源。 PLIC 有规范;CLINT 目前没有规范(特权级规范中部分描述了 CLINT 的行为),但是 Rocket 和 QEMU 中都使用了 SiFive 的设计;CLIC 也有规范,但是目前似乎只有 SiFive 的一些芯片使用了 CLIC 。

此外还有 ACLINT 规范,希望在兼容 CLINT 的基础上提供功能扩展,如 S 态的跨核软件中断。暂不清楚是否有实现。

尤予阳:我们或许可以在 ACLINT 中实现 S 态和 U 态的硬件时钟中断,甚至 U 态的跨核中断。

向勇:目前 PLIC 已经可以产生 U 态外部中断,能否将 CPU 的某个寄存器也关联到 PLIC ,向其中写入值的时候即在另一个核上触发外部中断?这在概念上是说得通的。

尤予阳:概念上确实可行,但是 PLIC 和 CPU 面向的是不同速度的总线和设备,在实现上可能会有点麻烦。

上一周的进展 #

贺鲲鹏:在 QEMU 中添加了 PLIC 的用户态上下文, rCore 中的 PLIC 驱动也做了相应的改动,现在理论上外设可以产生 UEI 了。

尤予阳:在 rCore 里面添加了内核对用户态中断上下文的保存恢复机制,以及用户态向内核注册中断的系统调用,但是还没写完。现在遇到的一个问题是,可能需要将外设对应的 MMIO 地址空间直接映射给用户态程序。

向勇:现在内核里面的内存属性分成了物理地址、IO 端口、虚拟地址,虚拟地址又分了恒等映射和分配器提供的映射,此外还有缓存和非缓存等等,以后在用户态可能也要引入类似的分类。

向勇:用户态的中断处理函数现在是怎么注册的?

尤予阳:在系统运行时里面提供保存和恢复上下文的汇编代码,以及一个默认的处理函数,默认的处理函数被标记为弱链接,如果用户实现了一个同名的函数,那么链接的时候就会跳转到用户的中断处理函数去。把入口地址写入 utvec 的代码也在系统运行时里面。

本周工作安排 #

在 rCore 里面实现内核对三种用户态中断的管理机制,以及相应的演示代码。可以简单一些,但要尽快做出来能够运行的程序。

复杂度与收益 #

陈渝:别人可能会提这样的问题:用户态中断和异步 OS 设计和实现的太复杂,不如还是用现有的方案。你们会怎么回答?

向勇:性能和安全性上的收益是现有的方案无法提供的。为了避免“熔断”攻击,我们需要把内核移到独立的地址空间;另一方面,一些驱动也不应该有权访问整个地址空间,因此要移到用户态和单独的页表;这两点都会带来性能损失,但如果有用户态中断和异步内核,性能可能还能超过原有的设计。

陈渝:如果我们能把性能提升做出来、说清楚,就是很好的成果了。

中断响应的实时性与性能 #

向勇:在现有的设计中,如果要最快速度响应一个中断,那就需要大量的保存和恢复现场开销;反过来,如果等到合适的时机再响应(如接收中断的进程被调度),那就会导致较大的延迟。如果想同时优化二者,那么就需要引入额外的硬件,把紧急但逻辑简单的工作交给硬件,而把较复杂的控制逻辑交给软件,例如由内核维护一个中断转发表,而由硬件进行转发。

我们延续将中断拆分为中断请求和中断响应两部分的思路,在 CPU 中实现一个硬件的中断缓冲区,当一个进程想给另一个进程发中断时,就将消息写入缓冲区,如果另一个进程在某个核上运行,那么可以自动产生中断;否则消息将停留在缓冲区中。进程切换时,一定会经过内核调度,此时内核将缓冲区中的内容转存到各个进程的 PCB 中,如果即将被调度运行的进程接收到中断,就进行中断注入。缓冲区满时,产生一个内核异常,由内核清理缓冲区。考虑到调度间隔一般在毫秒量级,缓冲区长度不需要太长。

这样设计的好处是,中断请求过程不需要内核介入,没有切换开销,并且当目标进程恰好在核上运行时,可以及时中断;目标进程不在核上运行时,那么无论如何需要到下次调度时才能响应,那么本设计至少不会比现有的其他方案更慢。