七. 中断

简介

中断是指CPU获知了计算机中发生的某些事,CPU暂停正在执行的程序,转而去执行处理该事件的程序,当这段程序执行完了之后,CPU继续执行刚才的程序。

通过中断可以极大的提高CPU的执行效率,如果没有中断,在处理器与外部设备通信时,他必须在向该设备发送指令后进入忙等待,反复轮询该设备是否就绪,这样就浪费了大量处理器的执行周期。引入中断之后,当处理器发出设备请求后就可以立即返回处理其他任务,而当设备动作完成之后,发送中断信号给处理器,后者就可以在完成动作之后回来获取处理结果

中断分类

按照中断事件来源分类,可以把中断分为外部中断和内部中断

外部中断

外部中断是指来自CPU外部的中断,而外部的中断源通常时某个硬件,所以外部的中断也叫硬件中断

由于外部设备在种类和数量上都非常多,CPU不可能为每个外部设备专门设计一个接口去处理他的中断信号。所以只能提供统一的接口作为中断信号的公共线路,所有来自外设的中断信号都共享公共线路连接都CPU。

其接口示意图如下

mark

从INTR引脚收到的中断都是不影响系统运行的,可以随时处理,他不会影响到CPU的执行。也称为可屏蔽中断。可以通过eflag中的if位将所有这些外部中断屏蔽

从NMI引脚收到的中断,通常是由于系统发生致命错误所导致的,比如电源掉电,内存读写错误等等。

内部中断

内部中断可分为软中断和异常

软中断是由软件主动发起的中断,他是软件主动发起的。

发起软中断有以下几种方式

  1. int 8位的立即数 通过它,我们可以发起系统调用,8位的立即数可以表示256种中断。处理器支持的中断也是256种

  2. int3 调试断点指令,在调式程序的时候,我们所下的断点就是通过他来实现的。通过调试器fork子进程,在断点处用int3替换原指令,从而使子进程调用int3触发中断

  3. into 中断溢出指令,只有在eflags中的OF位为1的情况下才会被触发

  4. bound 检查数组索引越界指令

  5. ud2 未定义指令,CPU遇到无效指令时触发该中断

异常是在指令执行期间CPU内部产生的错误引起的,由于是运行时错误,他不受eflags中的IF位的影响

异常按照轻重的等级,可分为以下三种

  1. Fault,也称故障。属于可被修复的一种类型,当发生此类异常时,CPU将机器状态恢复到异常之前的状态,之后调用中断处理程序,通常都能够被解决。缺页异常就属于此种异常

  2. Trap,也称陷阱。此异常通常在调试中。

  3. Abort,也称终止。程序发生了此类异常通常就无法继续执行下去,操作系统会将此程序从进程表中去除。

中断描述符表

中断描述符表是保护模式下用于存储中断处理程序入口的表,当CPU接受到一个中断时,需要根据该中断的中断向量号在此表中检索对应的描述符,在该描述符中找到中断处理程序的起始地址,然后执行中断处理程序

门描述符

在中断描述符表中存储的不只是中断描述符,还包括任务门描述符和陷阱门描述符。IDT中只有这种称为门的描述符,那么就要了解一下这些门的具体结构

任务门描述符

中断门描述符

中断门包含了中断处理程序所在的段选择子和段内偏移地址,当通过此方式进入中断后,标志寄存器eflags中的IF位自动置0,避免中断嵌套。linux就是利用的中断门实现的系统调用,也就是int 0x80。

中断门只允许存放在IDT中,而其他的门描述符则不然。

陷阱门描述符

在描述符中,通过S字段+type字段来确定描述符的具体类型

S字段为1时代表系统段,为0时代表数据段。此处的门描述符全部处于系统段下。

DPL表示描述符特权级


中断的处理过程

  1. 处理器根据中断向量号定位中断门描述符
  2. 处理器进行特权级检查
  3. 执行中断处理程序

这里重点要说一下特权级检查的过程。

为了防止3特权级下的用户主动调用只为内核服务的程序,当前的特权级必须经受的住中断门的考验,得“进入门内”。进门一般分为两步,“跨过门槛”,“进入门框”

“跨过门槛”的过程其实是检查 当前特权级(CPL)门描述符特权级(DPL) 的大小。要求 CPL权限大于等于DPL,在数值上 CPL<=DPL,“门槛”检查才通过,特权级的数值越小代表特权级越高。否则抛出异常

“进入门框”的过程实质是检查 CPL门描述符中选择子对应的目标代码段的DPL,要求 CPL权限小于目标代码段的DPL,在数值上 CPL>目标代码段DPL,此处不能相等。

如果该中断是一个软中断,也就是通过 int n, int3, into等引发的中断,执行上述两步的特权级检查

如果该中断是外部设备中断或者异常,只需要执行第二步的特权级检查

中断处理过程的示意图:

中断处理过程

中断发生时的压栈

在中断发生之后,处理器要去执行中断处理程序,该中断处理程序是通过中断门描述符中保存的代码段选择子和段内偏移找到的,也就是说需要重新加载段寄存器,那么为了能在中断处理完了之后还能返回当前进程,就必须保存当前进程的 CS:EIP,保存的地方当然就是中断处理程序的栈中了。因为中断可以在任意特权级下发生,所以当前进程的EFLAGS寄存器同样需要保存,如果涉及到特权级的变化,还需要压如SS和ESP寄存器

通过一副图来详细解释压栈的情况与顺序

中断压栈

图A、B:在发生中断是通过特权级的检测,发现需要向高特权级转移,所以要保存当前程序栈的SS和ESP的值,在这里记为ss_old, esp_old,然后在新栈中压入当前程序的eflags寄存器

图C、D:由于要切换目标代码段,这种段间转移,要对CS和EIP进行备份,同样将其存入新栈中。某些异常会有错误码,用来标识异常发生在哪个段上,对于有错误码的情况,要将错误码也压入栈中。

入栈说完了,在中断执行完了返回的时候通过指令 iret 完成,这个指令专门用于从中断返回

iret 指令会从栈顶依次弹出EIP、CS、EFLAGS,根据特权级的变化还有ESP、SS。但是该指令并不验证数据的正确性,而且他从栈中弹出数据的顺序是不变的,也就是说,在有error_code的情况下,iret返回时并不会主动跳过这个数据,需要我们手动进行处理

猜你喜欢

转载自blog.csdn.net/cw312644167/article/details/80051542