中断:
由于CPU获知了计算机中发生的某些事,CPU暂停正在执行的程序,转而去执行处理该事件的程序,当这段程序执行完毕后,CPU继续执行刚才的程序,整个过程称为中断处理,也称为中断。没有中断,操作系统几乎什么都做不了,操作系统就是中断驱动的。首先,操作系统就是个死循环,这个死循环什么都做不了,仅仅是保持操作系统能够周而复始的运行下去,运行的目的就是为了等待中断发生。
中断按事件来源分类,来自CPU外部的中断就称为外部中断,来自CPU内部的中断称为内部中断。
外部中断:
CPU提供了两条信号线。外部硬件的中断是通过两根信号线通知CPU的,这两根信号是INTR(可屏蔽中断)和NMI(不可屏蔽中断)
可屏蔽中断可以1,通过lags寄存器的IF位将所有这些外部设备的中断屏蔽。2,通过中断代理8259A来屏蔽单个设备的中断。
内部中断:
内部中断分为软中断和异常。
软中断:就是软件主动发起的中断,具有主观性,它来自于软件运行中指令int 8位立即数(系统调用)、 int3等
异常:指令执行期间CPU内部产生的错误引起的(除以0等)。不受eflags的IF影响,无法向用户隐瞒
总结:外部中断的NMI和内部中断都可以无视IF位。
中断向量号:
中断的本质就是来一个中断信号后,调用相应的中断处理函数。范围0-255。为每一个中断信号分配一个中断号,然后用此中断号作为中断描述符表的索引,找到对应的表项,进而在表项找到对应的地址,从而转去相应的处理程序。
异常和不可屏蔽中断的中断向量号是由CPU自动提供的,来自外部设备(时钟、键盘、硬盘)的可屏蔽中断号是由中断代理设置的,软中断是由软件提供的(int 8位立即数)。
中断描述符表
中断描述符表是保护模式下用于储存中断处理程序入口地址的表。中断描述符表中的中断描述符称为---门。Linux系统只用了中断门
中断门包含了中断处理程序所在段的段选择子和段内偏移地址。当通过此方式进入中断后,eflags中的 IF 位自动置0,即进入中断后,自动把中断关闭,避免嵌套。Linux就是利用中断门实现了系统调用:int 0x80。中断门只能在IDT中。
16位的表界限可以容纳中断描述符的个数为:64KB/8=8192,但是处理器只支持0-255。
中断处理过程及保护
完整的中断过程分为CPU外和CPU内两部分:
CPU外:外部设备的中断由中断代理芯片接受,处理后将该中断向量号发送到CPU
CPU内:CPU执行该中断向量号对应的中断处理程序
处理器内部的过程:
1,处理器根据中断向量号定位中断门描述符
处理器用中断向量号乘以8后与IDTR中的中断描述符表地址相加,所求的地址之和便是该中断向量号对应的中断描述符的线性地址,然后经过页部件翻译得到真实地址。
2,处理器进行特权级检查
若中断由软中断int n,int3(系统调用等)引起,则要进行当前的CPL和门槛DPL_Gate、门框:目标代码段DPL的检查。
若中断是外部设备和异常引起的,只检查当前CPL和目标代码段的DPL,数值上CPL > 目标代码段DPL。
3,执行中断处理程序
特权级检查后,将门描述符目标代码段选择子加载到代码段寄存器CS中,把门描述符中中断处理程序的偏移地址加载到EIP,开始执行中断处理程序。
中断发生后eflags中的 NT 位和 TF 位会被置 0 。用的是中断门的话eflags 的 IF 位会被自动置0,防止中断嵌套,即处理器会将这个中断完全执行完毕后才能出来下一个中断。从中断返回的指令是 iret ,它从栈中弹出数据到寄存器的 cs、eip eflags 等,根据特权级是否改变,判断是否恢复旧栈,即将SS_old 和 ESP_old 位置的值弹出到寄存器 ss和 esp。
中断发生时的压栈
特权级发生转移时:用户进程正在执行时候发生了中断
1,当前进程被中断打断后,处理器根据中断向量号找到对应的中断描述符,拿CPL和中断门描述符中选择子对应的目标代码段的DPL对比,若CPL权限比DPL低,即数值上CPL >DPL,表示要想高特权级转移,需要切换高特权级的栈。即:从用户进程中断去执行内核中断程序。中断执行完返回时也要回到之前的旧栈,所以处理器临时将SS_old 和 ESP_old 保存到一个内存中,然后CPU取出TSS中目标代码段的DPL 级别的栈加载到寄存器 SS 和 ESP 中,记作 SS_new 和 ESP_new,CPU在找到之前的旧栈,将其压入新栈中:将 SS_old 用0 扩展高16位,成为32位数据。
2,在新栈中压入EFLAGS寄存器
3,将要CS_old 和 EIP_old 压入新栈:CS_old 需要用0 来填充高16位为0。
4,某些异常有错误码,错误码用于报告哪个段上发生了异常,所以ERROR_CODE也压入栈。

特权级未发生转移时发生的中断:正在执行内核代码发生了中断
1,在新栈中压入EFLAGS寄存器
2,将要CS_old 和 EIP_old 压入新栈:CS_old 需要用0 来填充高16位为0。
3,某些异常有错误码,错误码用于报告哪个段上发生了异常,所以ERROR_CODE也压入栈。

中断返回的出栈:
用 iret 指令实现,iret 指令并不知道栈内容的正确性,一次弹出4字节,所以在使用iret 之前,一定要调整好esp的位置,使其依次弹出:EIP、CS、EFLAGS、ESP、SS。因此我们要手动跳过 ERROR_CODE。
1,当处理器执行到 iret 时,需要从栈中返回。CPU首先检查CS_old 的选择子的 RPL 位,判断在返回过程中是否要改变特权级。弹出 EIP_old 和 CS_old 。
2,弹出 EFLAGS。若需要改变特权级,则弹