中断与异常详解(二)

中断或异常发生之前

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

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

那么 CPU 将做以下事情

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

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

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

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

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

找到对应的门

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

堆栈变化

如果堆栈变化则将当前的(SS,ESP)压入栈中,此时的栈为内核栈,因为一旦出现中断或异常,堆栈就切换到了内核堆栈,上面这些操作是由硬件完成的,至于具体怎么操作的也没大明白,压内核栈不是得先更新成内核的SS和ESP吗?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 code,esi中错误码入栈


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


跳至从中断返回

时间: 2025-02-01 14:52:13

中断与异常详解(二)的相关文章

中断与异常详解(三)

再次梳理会用到的一些数据结构和名词 中断向量表(中断描述符表) idt_table 全局,8字节64位,从低到高位16位段选择符,32位偏移量,16位状态信息 256项 起始地址在内核数据节的idt中 用于寻找各种门,门的作用是防止用户程序访问陷阱门.中断门等特殊资源,出于安全考虑,linux为用户留有3,4,5,128号系统调用门供用户使用 中断描述符表寄存器 IDTR 寄存器,6字节48位,低16位界限,高32位基址 1项 用于快速寻找中断描述符,linux定义的16位界限为8*256-1,

中断与异常详解(五)

随着看的东西的增多,之前不明白的地方也开始有了眉目,所以更新前几节的东西,欢迎指正.想想马上就中断返回,进入进程描述了,还是挺激动的呢. 不得不推荐一篇写得很好的文章<spinlock的剖析与改进> http://www.searchtb.com/2011/06/spinlock%E5%89%96%E6%9E%90%E4%B8%8E%E6%94%B9%E8%BF%9B.html 中断向量表多达256项 虽然默认使用的只有49条,但很明显中断服务程序必须支持SMP,这也是在第三节为什么在 han

java异常详解

1. java中throw和throws:throw用在方法内部实际抛出异常的时候:throws用在方法头部(参数后面,方法体前面). public class Test{ public static void main(String[] args) { try { f(); } catch(NoSuchMethodException e) { System.out.println("test2"); } //用try-catch捕获并处理之后,后面的语句可以正常执行 System.o

java笔记--异常详解与处理

一.异常概念 Throwable类是Java中所有错误或异常的超类. 1.只有当对象是此类(或其子类)的实例时,才能通过Java虚拟机或着Java throw语句抛出.     2.只有此类或其子类才可以是catch字句中的参数类型.     3.有两个直接子类:Error和Exception         Error--指应用程序不应该去处理捕获的一种严重问题,常表示系统级的错误,如内存溢出        Exception--指程序需要捕获,需要处理的异常,是一种设计或实现方面的问题.  

UINavigationController详解二(转)页面切换和SegmentedController

原文出自:http://blog.csdn.net/totogo2010/article/details/7682433,非常感谢. 1.RootView 跳到SecondView 首先我们需要新一个View.新建SecondView,按住Command键然后按N,弹出新建页面,我们新建SecondView 2.为Button 添加点击事件,实现跳转 在RootViewController.xib中和RootViewController.h文件建立连接 在RootViewController.m

Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)

[Android布局学习系列]   1.Android 布局学习之——Layout(布局)详解一   2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)   3.Android 布局学习之——LinearLayout的layout_weight属性   4.Android 布局学习之——LinearLayout属性baselineAligned的作用及baseline    Layout Parameters(布局参数): 在XML文件中,我们经常看到类似与lay

CSS3中的弹性流体盒模型技术详解(二)

在上一篇文章<CSS3中的弹性流体盒模型技术详解(一)>里,我给大家列出了,从css1到css3各版本中盒子模型的基本元素.本篇我会把余下的属性进行详细讲解. box-pack 作用:用来规定子元素在盒子内的水平空间分配方式 box-pack 语法:box-pack: start | end | center | justify; start 对于正常方向的框,首个子元素的左边缘吸附在盒子的左边框显示 对于相反方向的框,最后子元素的右边缘吸附在盒子的右边框显示 end 对于正常方向的框,最后子

php学习之道:WSDL详解(二)

3.定义服务使用的逻辑消息 当服务的操作被调用时,服务被定义为消息交换.在wsdl文档中,这些消息被定义message元素.这些消息由称之为part元素的部分组成. 一个服务的操作,通过指定逻辑消息的方式来定义.当操作被调用时,逻辑消息被交换.(也就是说,逻辑消息代表了服务的操作)这些逻辑消息,将在网络上传输的数据定义为xml文档.他包含了所有的参数,这些参数是方法调用的一部分.(也就是说,逻辑消息里的参数,是操作对应方法的参数集合) 消息和参数列表:每一个被服务暴露的操作能且仅能有一个输入消息

LinearLayout详解二:从其父类View说起

这个View类说来就话长了,但我们又不得不说,要说呢,就得说的彻底,要让大家看得一清二楚,明明白白.所以我们就从源代码角度来看一个view是如何被加载的吧. 如果大家不知道怎么下载android的源代码,或者说懒得去下载(因为源代码确实比较大,大概有10G)的话,教大家几个取巧的办法: 1.直接在google中输入"android view.java"即可.这种方法成功率非常高,一般android的比较重要的类都能搜到. 2.给大家提供一个人家用于放源码的的git:[email pro