码迷,mamicode.com
首页 > 其他好文 > 详细

中断与异常详解(二)

时间:2015-10-23 01:24:15      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:

中断或异常发生之前

CPU 执行了当前指令之后,CS EIP 这对寄存器中所包含的内容就是下一条将要执行 指令的逻辑地址。在对下一条指令执行前,CPU 先要判断在执行当前指令的过程中是否发生 了中断或异常

如果发生了一个中断或异常

那么 CPU 将做以下事情

• 确定所发生中断或异常的向量i(在 0~255 之间)。

• 通过 IDTR 寄存器找到 IDT 表,读取 IDT 表第i项(或叫第i个门)。

分两步进行有效性检查:首先是“段”级检查,将 CPU 的当前特权级 CPL(存放在 CS 寄存器的最低两位)与 IDT 中第i项段选择符中的 DPL 相比较,如果 DPL3)大于 CPL0), 就产生一个“通用保护”异常(中断向量 13),因为中断处理程序的特权级不能低于引起中 断的程序的特权级。这种情况发生的可能性不大,因为中断处理程序一般运行在内核态,其 特权级为 0。然后是“门”级检查,把 CPL IDT 中第 i个门的 DPL 相比较,如果 CPL 大于 DPL,也就是当前特权级(3)小于这个门的特权级(0),CPU 不能“穿过”这个门,于是 产生一个“通用保护”异常,这是为了避免用户应用程序访问特殊的陷阱门或中断门。但是 请注意,这种“门”级检查是针对一般的用户程序,而不包括外部 I/O 产生的中断或因 CPU 内部异常而产生的异常,也就是说,如果产生了中断或异常,就免去了“门”级检查

(这里的免去没大明白,是不进行有效性检查了?直接跳过这一步?)

检查是否发生了特权级的变化。当中断发生在用户态(特权级为 3),而中断处理程 序运行在内核态(特权级为0),特权级发生了变化,所以会引起堆栈的更换。也就是说,从 用户堆栈切换到内核堆栈。而当中断发生在内核态时,即 CPU 在内核中运行时,则不会更换堆栈

 

 

找到对应的门

异常处理没说怎么在idt_table中找到对应的门的,中断倒是因为有中断号,可以根据这个中断号去idt_table中找到门,反正cpu硬件干的

堆栈变化

技术分享

如果堆栈变化则将当前的(SS,ESP)压入栈中,此时的栈为内核栈,因为一旦出现中断或异常,堆栈就切换到了内核堆栈,上面这些操作是由硬件完成的,至于具体怎么操作的也没大明白,压内核栈不是得先更新成内核的SSESP吗?ESP更新了,那压的ESP不就是内核的?看见书中有提到此时的内核堆栈是空的,也许压的固定位置吧。看到后面搞懂了再更新。

更新:有看到说从TSS中取到的内核堆栈,难道是用movl实现的?

 

 

处理程序格式对于异常和中断是不同的

 

异常处理

handler_name:

处理程序名称如:debug,nmi,int3,overflow,bounds等异常名,与异常类型相对应

pushl $0 /* only for some exceptions */

没有错误码的需要压一个0,使内核堆栈保持一致性

pushl $do_handler_name

将真正的处理函数地址压栈

jmp error_code

跳至公共异常处理

此时堆栈状态

技术分享

 

error_code:

公共异常处理

pushl %ds

ds入栈

pushl %eax

eax入栈

xorl %eax,%eax

eax清零

pushl %ebp

ebp入栈

pushl %edi

edi入栈

pushl %esi

esi入栈

pushl %edx

edx入栈

decl %eax

# eax = -1

pushl %ecx

ecx入栈

pushl %ebx

ebx入栈

cld

eflag中的DF标志,使EIP朝向增长方向

movl %es,%ecx

es移入ecx

movl ORIG_EAX(%esp), %esi

# get the error code,移入esi

movl ES(%esp), %edi

# get the function address,上面压栈的do_handler_name函数的地址,移入edi

movl %eax, ORIG_EAX(%esp)

-1移入esp+ORIG_EAX的位置,对应原来的错误码的位置

movl %ecx, ES(%esp)

ecx中的值(es)存入esp+ES的位置,即是ES本该存的地方

movl %esp,%edx

将当前的堆栈地址存入edx

pushl %esi

# push the error codeesi中错误码入栈

pushl %edx

# push the pt_regs pointer,将现场信息的起始地址压栈,类似于pt_reg的作用,使异常处理程序可以按照统一规则去访问出错现场的数据

movl $(__KERNEL_DS),%edx

读取内核数据段

movl %edx,%ds

加载内核数据段

movl %edx,%es

加载内核ES

GET_CURRENT(%ebx)

将当前进程的task_struct存入ebx,中断返回进程调度和信号处理需要task_struct中的信息,而且只能在内核态操作,因为task_struct存在内核底部

call *%edi

call执行真正的异常处理程序

addl $8,%esp

真正的异常处理程序返回后丢弃错误码和异常处理程序地址

jmp ret_from_exception

跳转异常返回,还需对进程调度,信号处理,vm模式和是否返回用户态进行处理,恢复现场

 

技术分享

 

技术分享

 

中断处理

技术分享

预处理后的结果

IRQn_interrupt:

中断号为n的中断处理程序

pushl $n-256

将中断号入栈,与异常处理的硬件自动压栈错误码或者手动压0相对应

jmp common_interrupt

跳至公共中断处理

技术分享

预处理后的结果(因为加了asmlinkage标识,所以do_IRQ会在栈中寻找参数,即pt_regs就是ESP

common_interrupt:

公共异常处理

SAVE_ALL

保存现场到栈中,作为pt_regs参数,与公共异常处理前半截压栈操作相对应,异常处理程序还需将错误码和真正的异常处理程序地址取出来,将es存入正确位置,然后才调用真正的异常处理程序进行处理,同样的采用栈传参数的方式,传入的是*pt_regs和错误码两个参数,只是第一个参数是ESP地址,而中断的第一个参数取到的就是ESP

call do_IRQ

call调用中断处理程序

jmp ret_from_intr

跳至从中断返回

技术分享

中断与异常详解(二)

标签:

原文地址:http://www.cnblogs.com/hmxb/p/4903208.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!