1、中断和异常的区别
中断:一般由外部设备引起;
异常:cpu执行完某条指令之后,cpu控制单元产生,一般由编程错误引起。
2、中断向量
0~255
0~31:异常和不可屏蔽中断
32~47:可屏蔽中断
48~255:软中断
linux使用一个软中断(0x80)作为系统内核函数的系统调用接口。
硬件中断IRQ0~IRQ15被关联到了中断向量32.。。47.
2、中断描述表
中断描述符表中,每个向量在表中有相应的中断或者异常处理程序的入口地址。内核在允许中断发生前,必须先适当的初始化IDT
中断描述符表的作用:将中断向量和中断处理程序一一对应起来。
IDT表的大小为:256*8.因为linux系统中有256个中断和异常,所以该表共有256项,每一项有8个字节。
16-31是段选择符,0-15和48-64组合起来形成32位偏移量。
40-43表示描述符的类型:01110中断描述符;00101任务门描述符,01111陷阱门描述符。在这里我们主要研究中断门和陷阱门。而这个描述符也是中断门和异常门唯一的区别。
DPL:等于0或者3.0是特权模式。用户态(DPL=3).
当前的执行等级被保存在CPL寄存器中。控制单元比较CPL和IDT中的DPL字段,如果DPL字段的值较大,则中断被执行。
中断描述符表是存在于内存中的,因此,怎么寻址到这张表的呢?
linux中有一个寄存器IDTR(48位)其中32位是段基址,16位是段限。(从这里我们可以看出,整个IDT表的大小为64k,而每个表项是8字节,所有最多有8k的表项,但是系统中只有256个表项被使用)
linux中有一个GDT表,记录了所有的段基址,段属性以及段长。
我们是怎么根据中断向量号(0~255)找到中断服务程序的入口地址呢?
在IDT中,每个表项都是一个门。我们首先根据中断号找到中断门,然后根据中断门中的段选择符(16位:高十三位是索引,最低两位是DPL),索引到GDT中的相应表项。从而找到该中断服务程序所在的段基址。然后该段基址和中断门中的偏移量找到中断服务程序入口地址。
在这里有一个安全性的问题。当找到段基址之后,该段基址中有一个DPL,我们需要将当前特权级CPL(存放在cs寄存器中的低两位)与段描述符(存放在GDT中)的描述符的特权级DPL进行比较,如果CPL比较小,则产生异常。
对于编程异常,则做进一步安全检查,比较CPL与处于IDT中的门描述符中的DPL,如果DPL小,则产生一个“General protection”异常。
对于上面这句话总结一下:意思是在中断门(异常门)中,有两个DPL,分别位于段选择符以及(46,45bit)中。
我们都知道这个段选择符最终是要装载到cs寄存器中,而偏移量是要装载到eip寄存器中。那么这个中断到底能不能执行,我们首先要将这个段选择符中的DPL和目前cs寄存器中的CPL进行比较(低两位);
如果要还要考虑编程异常,那么该中断门中的DPL也要进行相应的检查。
linux中有很多体系结构相关的函数在IDT中插入门:
set_trap_gate(n,addr).
在IDT的第n个表项插入一个陷阱门。门中的段选择符设置成为内核代码的段选择符,偏移量设置为异常程序处理地址(addr),同时DPL字段设置为0.
在linux内核源码的实现上,idt_descr变量(6个字节制定了IDT的位置和大小)。idt_table存放了IDT表。
在linux内核中,首先用一个空函数ignore_int来统一初始化中断处理程序,然后在start_kernel中对IDT进行第二次初始化。trap_init()函数将异常处理程序插入到IDT中。插入的函数包括:
set_trap_gate()(DPL=0),set_intr_gate()(DPL=0),set_system_gate()(DPL=3,陷阱门),set_system_intr_gate()(DPL=3),set_task_gate()(中断门,门选择符是TSS全局描述符表的指针,该TSS中包含被激活的函数,偏移量设置为0,DPL=3),
异常处理程序三个标准的结构:
在内核堆栈中保存大多数寄存器的内容;
用高级c语言处理异常;
通过ret_from_exception()函数从异常处理程序退出。
分析:int指令发送一个中断信号,然后,系统就会查找IDT->GDT,找到异常处理程序,执行完之后返回。
中断:
IO中断;时钟中断;处理器间中断;
中断处理程序的灵活性:
IRQ共享;
IRQ动态分配;
中断处理程序要执行的操作:紧急的;非紧急的;非紧急可延迟的;
IO中断处理流程:IRQn->IDT[32+N]->interrupt[n]->do_IRQ[n]->中断服务例程1。。。中断服务例程n。
每一个中断向量(中断门)对应一个irq_desc描述符。
如果一个中断内核没有处理(该中断线上的中断服务例程不存在或者与该中断线相关的所有例程都识别不出是否是自己的硬件设备发出的中断)
在irq_desc_t中irq_count和irq_unhandled字段表明了中断和意外中断的次数。如果当100000次中断发生时,意外中断的次数超过了99900,则禁用这条中断线。