forked from rcore-os/rCore
-
Notifications
You must be signed in to change notification settings - Fork 26
usr mode interrupt
Jason Yu edited this page Apr 2, 2019
·
9 revisions
目前x86/arm/rv在硬件上都没有全面的支持usr-mode interrupt(umi),但相对而言rv已经有相关考虑了。 目前的想法是:
-
外设产生一个中断发送到中断控制器
-
把中断控制器这个中断发送给CPU interface
-
CPU interface打断当前执行(当前可能在用户态,也可能在内核态。不过没关系)告诉OS去处理这个中断(在OS执行中断处理例程开始处,可能中断是被屏蔽了,OS需要在合适的时候enable中断)。
- 3.1 如果在用户态被外设中断打断,会有一个被打断点:此时执行 4. 步骤
- 3.2 如果在内核态被外设中断打断 思路一(eager method): 会有多个个被打断点: 1)用户态被打断点(可能是用户主动syscall/产生了异常,或者用户态产生了中断,进入了内核态,这时的用户态被打断的地方)2)1个或多个内核态被打断点。所以os需要在内核直接执行drv的中断服务例程。然后恢复os被打断部分继续执行。 执行时序:os intr handler --> usr drv intr handler -->user drv other parts -->application OR into kernel again...
- 3.2 如果在内核态被外设中断打断 思路二(lazy method):我们的os架构支持 drv的不同代码部分即可在用户态执行,也可在内核态执行,那么drv的中断处理例程在内核态处理好了,比如设置好标记,让用户态驱动在获得控制权后能改执行后续处理。处理完毕后,内核返回得到用户态代码。执行时序:os intr handler --> 设置对usr drv的通知机制 --> 内核继续处理相关事务,直到必须要返回到用户态了(如果没进程ready,唤醒0号drv用户态 drv server)--> usr drv intr handler -->user drv other parts -->application OR into kernel again...
-
OS对这个中断进行检查,发现这个中断打断了用户态的执行,是送给usr mode drv处理的,它会设置一个虚拟中断,将物理中断和虚拟中断连接在一起,把这个虚拟中断涉及的信息(中断号, 等....)加入到usr mode drv的堆栈中(模拟出一个用户态中断产生了)。
-
OS执行sysret(注意:没用iret, 因为syscall, sysret是x86-64的标配),返回到usr mode drv的中断处理例程
-
usr mode drv的中断处理例程进行处理,处理之后直接返回到被打断的用户态代码继续执行。
上面的方法存在一个问题:返回到user mode drv的时候,中断使能应该被打开还是保持关闭?
- 如果保持关闭:user mode drv在完成执行并返回application之前需要重新将其打开。两种办法:
- 通过syscall回到内核完成
- 内核中设置IOPL为3,这样就允许在用户态开关中断使能了。这种办法显然没有syscall方法安全,首先因为syscall可以通过判断
eip
寄存器,软件形式增加一层权限检查,而硬件并不会检查是在user mode driver还是在application执行指令,其次因为syscall可以设计为只能开中断,这对于application而言其实是没有影响的
- 如果开启:实际的中断处理变成可被抢占的,但是中断处理本身并不在process context中,不应该被其他线程抢占。被其他外部中断任意抢占的问题
- 实时性
- 栈空间
如果进入用户态之后还需要回到内核态,这种方式据我所知基本上就是upcall了,也就是内核调用用户空间的函数。upcall的优点一般认为不在效率或安全,而在灵活性。
另一种方法
- 通过改变IDT设置,产生相应中断时直接跳到用户态,此时全局中断使能是关闭的,在结束处理时再通过一个特殊的系统调用返回并恢复中断(类似于Linux信号机制中的
SIGRET
系统调用 - 中断栈:x86-64允许通过TSS中的Interrupt Stack Table设置使某种中断切换到特殊的栈。可以为所有在用户态处理的中断设置一个特殊的栈,这样应该能够安全点
- 如果中断是在内核产生的。两种方式
- 还是跳到用户态
- 可能更高效的方式:进入内核时通过重新设置IDT使该中断改为在内核中执行
- 与设备交互:
- 读写参考一般的用户态驱动,即映射BAR空间后直接读写
- interrupt ack:暴露LAPIC寄存器?
问题
- 设计的优劣问题到底应该怎么考虑。是尽量在用户态,还是尽量特权级切换少
- Linux通常将中断的处理分成上下两部分。上部在interrupt context中,中断禁用,不可抢占。下部则可以阻塞、睡眠,可以放在process context中(由内核线程进行调度处理)。这种设计对我们的问题有什么借鉴意义么
修改qemu,参考m-mode/s-mode的实现,让其支持u-mode的中断处理。然后让rcore能在其上运行。
- 物理机试验x86-64以下方面的特性
- 通过设置IDT中entry的段选择子RPL可以使用户态产生的中断跳到用户态的interrupt handler
- 通过trap gate和interrupt gate可以控制到用户态interrupt handler时是否屏蔽中断
- 通过设置IOPL=3可以允许用户态直接开关中断
- 查x86手册发现执行高特权级代码时产生的中断确实无法直接在用户态处理(Vol.3 6.12.1.1)
- 进出内核时可能需要改写IDT或直接屏蔽需要用户态处理的中断
- 其他类型的门研究(call gate?)
- 类似signal、在process context下处理中断的设计:
- 产生中断时切到相应进程,调用其注册的handler处理
- 优化的一个地方:当该进程正好在运行且处于用户态,直接跳过内核态处理(需要IDT和IOAPIC设置配合?)
- 两种传统的process context下处理中断的方式
- 单独的处理线程轮询(阻塞)
- 类似signal:处理中断和进行其他计算的代码放在同一个线程中,当收到中断时线程切到中断处理的代码执行
- CR3无法读写
- IOPL=3时可IN,INS,OUT,OUTS,STI,CLI
- TSS中设置I/O Permission Bit Map可以针对特定端口地址设置I/O权限