【归纳】
1、中断和异常机制会产生向量号。(中断的向量号:通过8259A可编程中断控制器产生的IRQ与向量号相关联)
2、向量号会作为IDT中的索引来取出门描述符。向量号与IDT表一一对应。如向量号0对应着IDT表中的第一个门描述符。
3、软中断指令“INT n”和“INTO”也归类于异常而不称为中断。(CPU外部的叫中断,CPU内部的叫异常)
异常分为3类:故障& 陷阱 & 中止
异常有两种产生方式:cpu自动产生(指中断向量号的前13h个是CPU产生的)&Int指令(也叫软中断)
4、IDT中的描述符可以是 中断门描述符 &陷阱门描述符 & 任务门描述符
【Start】
1 IDT是个描述符表,叫做中断描述符表。IDT中的描述符可以是下面三种:
调用门和中断门与陷阱门的区别:
1 对比调用门在中断门和陷阱门中的低5位变成了保留位,而不再是ParamCount,而且TYPE的4位也将变成0xE(中断门)或0xF(陷阱门)。当然S位仍然是0。
2 它们之间存在着一个微小的差别,就是对终端允许标志IF的影响。由中断门向量引起的中断会复位IF,因为可以避免其他中断干扰当前中断的处理。随后的iret指令会从堆栈上恢复IF的原值,而通过陷阱门产生的中断不会改变IF
2 中断需要建立硬件中断与向量之间的对应关系,通过编程操作8259A芯片实现
在保护模式下,CPU将中断向量号和IDT做了对应,通过lidt指令(lidt即是设置lidt的地址和大小,指令LIDT和SIDT分别用于加载和保存IDTR寄存器的内容,中断向量就是存在IDT中的地址偏移.中断向量表就是记录在IDTR寄存器中的地址及长度这一段内存数据.)
14h-19h供intel保留
程序员通过对8259A芯片编程将外部中断IRQ中断与20h-2fh相对应,
使的当外部产生中断时,能映射到向量号上
30h-0FFh是程序员自己可定义的软中断,通过Int调用即可
【CPU外部的叫中断】
程序员通过对8259A芯片编程将外部中断IRQ中断与20h-2fh相对应
【CPU内部的叫异常】
异常分为3类:故障 &陷阱 &中止
异常有两种产生方式:cpu自动产生(指中断向量号的前13h个是CPU产生的)&Int指令(也叫软中断)
中断&异常的处理程序都在IDT中,idt有陷阱门&中断门两种门描述符
【详细介绍】
按中断事件来源进行分类,主要有两类:
(1)中断。由CPU以外的事件引起的中断,如I/O中断、时钟中断、控制台中断等。
(2)异常(exception)。来自CPU的内部事件或程序执行中的事件引起的过程。如由于CPU本身故障、程序故障和请求系统服务的指令引起的中断等。
异常
指令执行期间检测到不正常的或非法的条件所引起的,异常与正执行的指令有直接的联系。例如,执行除法指令时,除数等于0。再如,执行指令时发现特权级不正确。当发生这些情况时,指令就不能成功完成。软中断指令“INT n”和“INTO”也归类于异常而不称为中断,这是因为执行这些指令产生异常事件。
80386识别多种不同类别的异常,并赋予每一种类别以不同的中断向量号。异常发生后,处理器就象响应中断那样处理异常。即根据中断向量号,转相应的中断处理程序。把这种中断处理程序称为异常处理程序可能更合适。
根据引起异常的程序是否可被恢复和恢复点不同,把异常进一步分类为故障(Fault)、陷阱(Trap)和中止(Abort)。我们把对应的异常处理程序分别称为故障处理程序、陷阱处理程序和中止处理程序。
(参照上图中异常类型)
A 故障是在引起异常的指令之前,把异常情况通知给系统的一种异常。80386认为故障是可排除的。当控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令。这样,在故障处理程序把故障排除后,执行IRET返回到引起故障的程序继续执行时,刚才引起故障的指令可重新得到执行。这种重新执行,不需要操作系统软件的额外参与。故障的发现可能在指令开始执行之前,也可能在指令执行期间。如果在指令执行期间检测到故障,那么中止故障指令,并把指令的操作数恢复为指令开始执行之前的值。这可保证故障指令的重新执行得到正确的结果。例如,在一条指令的执行期间,如果发现段不存在,那么停止该指令的执行,并通知系统产生段故障,对应的段故障处理程序可通过加载该段的方法来排除故障,之后,原指令就可成功执行,至少不再发生段不存在的故障。
B陷阱是在引起异常的指令之后,把异常情况通知给系统的一种异常。当控制转移到异常处理程序时,所保存的断点CS及EIP的值指向引起陷阱的指令的下一条要执行的指令。下一条要执行的指令,不一定就是下一条指令。因此,陷阱处理程序并不是总能根据保存的断点,反推确定出产生异常的指令。在转入陷阱处理程序时,引起陷阱的指令应正常完成,它有可能改变了寄存器或存储单元。软中断指令、单步异常是陷阱的例子。
C中止是在系统出现严重情况时,通知系统的一种异常。引起中止的指令是无法确定的。产生中止时,正执行的程序不能被恢复执行。系统接收中止后,处理程序要重新建立各种系统表格,并可能重新启动操作系统。硬件故障和系统表中出现非法值或不一致的值是中止的例子。
中断通常在程序执行时因为硬件而随机发生,它们通常用来处理处理器外部的事件,比如外部设备的请求。软件通过执行int n指令也可以产生中断。
异常则通常在处理器执行指令过程中检测错误时发生。比如遇到除零的情况。异常又分为错误,陷阱和终止。
不管中断还是异常,通俗来讲,都是软件或硬件发生了某种情形而通知CPU的行为。
在保护模式中,中断机制发生了很大的变化,原来的中断向量表已经被IDT所代替,实模式下能用的BIOS中断在保护模式下已经能够不能用了。
【OTHER】
IDT中的描述符可以是中断门描述符 &陷阱门描述符 &任务门描述符
85h这是门描述符的4,5字节描述属性
DA_TaskGateEQU 85h ; 任务门类型值(存入IDT中)
DA_386IGateEQU 8Eh ; 386 中断门类型值(DPL为0,不允许用户态直接使用int指令访问,但可以在R0时可以通过Int n形式访问)
DA_386TGate EQU 8Fh ; 386 陷阱门类型值(用于系统调用,DPL为3,允许用户态直接使用int指令访问)
DA_386CGateEQU 8Ch ; 386 调用门类型值(存于GDT中)
中断门和陷阱门其它的区别:就是对中断允许标志IF的影响,由中断门向量引起的中断会复位IF(位于CPU标志寄存器中),因为可以避免其他中断干扰当前中断的处理。随后的iret指令会从堆栈上恢复IF的原值;而通过陷阱门产生的中断不会改变IF。
中断描述符表IDT
与8086/8088一样,在响应中断或者处理异常时,80386根据中断向量号转对应的处理程序。但是,在保护模式下,80386不使用实模式下的中断向量表,而是使用中断描述符表IDT。在保护模式下,80386把中断向量号作为中断描述符表IDT中描述符的索引,而不再是中断向量表中的中断向量的索引。
象全局描述符表GDT一样,在整个系统中,中断描述符表IDT只有一个。中断描述符表寄存器IDTR指示IDT在内存中的位置。由于80386只识别256个中断向量号,所以IDT最大长度是2K。(256*8,一个门描述符占8个字节)
中断描述符表IDT所含的描述符只能是中断门、陷阱门和任务门。也就是说,在保护模式下,80386只有通过中断门、陷阱门或任务门才能转移到对应的中断或异常处理程序。
中断描述符表(IDT):256项,其中的每一项关联一个中断/异常处理过程,有三种类型(门类型):
- Task Gate Descriptor. 用于任务切换。
- Interrupt Gate Descriptor.用于处理中断。
- Trap Gate Descriptor. 用于处理异常。
- 任务门:用于任务切换
- 中断门: 用于硬件中断,DPL为0,不允许用户态直接使用int指令访问,但可以在R0时可以通过Int n形式访问(书中有例子)
- DPL3陷阱门: 用于系统调用,DPL为3,允许用户态直接使用int指令访问。这样才能通过int2e访问系统调用(WINDOWS的系统调用int2e,unix系统调用int80),INT 3用于断点指令(又称为软件异常)
- DPL0陷阱门: 用于CPU异常(又称为硬件异常),不允许用户态直接使用int指令访问,在R0时可以通过Int n形式访问(书中有例子)
注释:INT 3会导致CPU执行nt!KiTrap3()例程,在系统引导的时候,WINDOWS会填充IDT,其中包含了指向内核中负责处理每个中断和异常的例程的指针(JK:自己写系统时候也是要先填充IDT的)
在指令执行过程中控制单元检测是否有中断/异常发生,如果有,等待该条指令执行完成以后,硬件按如下过程执行:
- 确定中断向量的编号i。
- 从IDT表中得到第i个门描述符。(idtr指向IDT)
- 由第i项中的选择符和gdtr查到位于GDT中的段描述符,从而得到中断处理程序的基地址,而偏移量位于门描述符中。
- 做权限检查:比较cs中的CPL和GDT中段描述符的DPL,确保中断处理程序的特权级不低于调用者。对于programed exception 还需检查CPL与门描述符的DPL,还应确保CPL大于等于门的DPL。Why?因为INT指令允许用户态的进程产生中断信号,其向量值可以为0到255的任一值,为了避免用户通过INT指令产生非法中断,在初始化的时候,将向量值为80H的门描述符(系统调用使用该门)的DPL设为3,将其他需要避免访问的门描述符的DPL值设为0,这样在做权限检查的时候就可以检查出来非法的情况。
- 检查是否发生了特权级的变化,一般指是否由用户态陷入了内核态。
- 如果是由用户态陷入了内核态,控制单元必须开始使用与新的特权级相关的堆栈
- a. 读tr寄存器,访问运行进程的tss段。why?因为任何进程从用户态陷入内核态都必须从TSS获得内核堆栈指针。
- b. 用与新特权级相关的栈段和栈指针装载ss和esp寄存器。这些值可以在进程的tss段中找到。
- c. 在新的栈(内核栈)中保存用户态的ss和esp,这些值指明了用户态相关栈的逻辑地址。
- 若发生的是故障,用引起异常的指令地址修改cs和eip寄存器的值,以使得这条指令在异常处理结束后能被再次执行
- 在栈中保存eflags、cs和eip的内容
- 如果异常带有一个硬件出错码,则将它保存在栈中
- 装载cs和eip寄存器,其值分别是在GDT中找到的段描述符段基址和IDT表中第i个门的偏移量。这样就得到了中断/异常处理程序第一条指令的逻辑地址。
从中断/异常返回:
中断/异常处理完后,相应的处理程序会执行一条iret指令,做了如下事情:
1)用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码
2)检查处理程序的特权级是否等于cs中最低两位的值(这意味着进程在被中断的时候是运行在内核态还是用户态)。若是内核态,iret终止执行;否则,转入3
3)从栈中装载ss和esp寄存器。这步意味着返回到与旧特权级相关的栈。
4)检查ds、es、fs和gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且特权级比当前特权级高,则清除相应的寄存器。这么做是防止怀有恶意的用户程序利用这些寄存器访问内核空间。
中断就是硬件对CPU发出的一个信号.
中断向量就是存在IDT中的地址偏移.
中断向量表就是记录在IDTR寄存器中的地址及长度这一段内存数据255*64(每个门描述符占64个字节).