中断笔记

有关于中断的流程

对于中断的产生,有4步流程。

操作系统的中断一共有256个。分别从0~255。

【0-31】故障,陷阱亦或是外设中断中的非屏蔽中断(NMI)。

【32-47】这期间的16个门描述符,对应着我们外设I/O设备的中断信号IDTR。这16个中断是靠8259A芯片来控制的。毕竟I/O设备一定会产生字符请求,CPU不可能随时都盯着他们来看,如果CPU疯狂地轮询I/O设备,那还了得。所以这16个中断主要是8259A芯片来承接,产生中断来对应I/O设备的。

【48-255】剩下的所有都是软件中断了。像是Linux内部,就设置了INT 80为系统中断调用。

  1. 微机8259A芯片的编程接口初始化。正常情况下,8259A芯片一共有主从两片,是为级联结构。在ucore初始化为pic_init()函数。

  2. 当8259A芯片初始化之后,我们就需要创建中断门描述符表(IDT)了。中断门描述符表中一共有256个门描述符(GD),门描述符可以分为四种,分别是调用门描述符(CGD),中断门描述符(IGD),陷阱门描述符(TGD)以及任务门描述符(也是TGD,但是ucore不涉及)。这个时候准备工作已然充分,这个时候我们应该设置中断处理例程(ISR)了。当然之前要对ICW1进行设置,把mask设为FF即全1,也就是屏蔽所有中断。顾名思义,就是触发中断之时所执行的函数。最后,使用IDTR汇编指令对其进行设置。

    2.5 有关于CPU对某一中断号进行寻址并且调用ISR的手段:

    CPU在每执行一条指令的时候,对于【32-47】号中断向量来说,CPU执行完毕一条指令,就会查看8259A芯片的I/O缓冲当中是否有保存的中断信号。如果没有,CPU会执行下一条指令。如果有,那么CPU就会得到此中断信号,然后把此中断信号*8,这时得到的就是中断向量的索引(因为每一个中断门描述符是8字节64bit存放,而IDT和GDT一个明显的区别就是,GDT的第一个表项只能是8字节的全0,而IDT的第一个表项可以是正常的IDT表项)因此,每个中断向量在IDT的索引(偏移值)就是【中断信号量*8】。因而,藉由为IDT专门设计的寄存器IDTR,就可以查到IDT的位置;从中断向量的偏移值,我们就可以查到那个中断向量对应的门描述符。由这个门描述符,从16-31位CPU就会得知此中断向量对应的ISR所在的段选择子,也就是在GDT的偏移量,这样我们就可以定位到那个ISR所在的段了。由此,CS寄存器的值会被确定。由0-31位和48-63位CPU可以确定ISR在这个段中的OFFSET,这样EIP的值也被确定,这样,我们很完美地索引到了ISR的真正位置!!

    但是这其实是后边的步骤了。其实这个寻找之前,我们需要先判断权限。细心的童鞋会发现我们门描述符的32-47位并没有被使用。而这16位中就蕴含着很重要的“属性”。比如判断这个门描述符的真正分类到底是上述四种中的哪种?IGD?TGD?还是CGD?这个信息由这其中的位能够说明。更重要的是这16位中的后2-3位,即第45-46位。这两位代表“DPL”值,也就是此门描述符的特权优先级。这个优先级非常有意思。CPU回先比对CS段寄存器内部的“CPL”位(同样应该是两位,因为权限等级从ring0-ring3嘛。0二进制是00b,3二进制是11b。因此势必需要两位),然后会把这个位和DPL进行比较,来进行判断是否“有权限”来执行这个中断。=>打一个比方来说,如果此时进行的是外设中断,即硬件中断,那么这个优先级是非常高的。如果这时目前CPU所执行的进程位于用户态ring3(目前进程的运行权限级别由TSS段来获得,该信息在内存起始位置在TR寄存器中 ——原话),那么这个时候,CPU会强制把用户栈升级成为内核栈,然后来进行ISR的执行。但是在此之前,必须要先保存用户态的信息。也即是,CPU会保护现场,依次压入ss,esp(前二者如果原先就在内核态则不会压入),eflags,cs,eip以及errorCode(可选)。这样的话,当弹栈的时候可不要忘记倒着弹出来啊。之后刚才我们已经得到了ISR的真·地址了。这样我们就把CS和EIP设置为要设置的值就ok啦。然后就执行ISR咯。当然我们不要忘记那个errorCode。如果errorCode有值的话,那么一定在编程的时候把errorCode先弹出来进行处理咯。

    在ISR调用结束之后,CPU要进行恢复现场。这样,会倒着弹出来之前eip,cs,eflags。如果原先是用户态,那么就会继续弹出来esp和ss,并且切换回用户栈。如此一来,用户栈和内核栈对于外部中断的切换就算完成~

    =>如果是软件中断,因为CPU会禁止用户瞎玩弄中断(可能是怕把中断玩坏吧,大雾),因此和硬件外设中断恰恰相反,只有在当前进程的CPL比DPL的等级要不低于的时候(当然数值要不高于,正好相反),这样ISR才会执行。这样,就是软件中断的执行情况了。

  3. 有关于外设的中断。
    有许多外设需要中断。比如scanf的实现。而printf并不需要(如果是直接往显存中写的话)而如果是向串口和并口中写入的话,也是需要的。scanf是需要与外部的串口设备tty相连的,而且需要键盘keyboard产生中断。(键盘在外设中断的偏移值是1,即中断号33,tty的外设中断偏移值是4,即中断号36)。还有时钟中断,偏移值是0,中断号32。对于他们,我们就需要进行对相关的设备进行端口设置之后,把前文所说的mask进行“打开”,这样就可以接收中断了。这样的话,我们就可以通过打开相应的PIC的mask位,然后允许相应中断的执行了。