欢迎来到冰点文库! | 帮助中心 分享价值,成长自我!
冰点文库
全部分类
  • 临时分类>
  • IT计算机>
  • 经管营销>
  • 医药卫生>
  • 自然科学>
  • 农林牧渔>
  • 人文社科>
  • 工程科技>
  • PPT模板>
  • 求职职场>
  • 解决方案>
  • 总结汇报>
  • ImageVerifierCode 换一换
    首页 冰点文库 > 资源分类 > DOCX文档下载
    分享到微信 分享到微博 分享到QQ空间

    第5章 中断和异常Word下载.docx

    • 资源ID:5763781       资源大小:82.37KB        全文页数:47页
    • 资源格式: DOCX        下载积分:3金币
    快捷下载 游客一键下载
    账号登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录 QQ登录
    二维码
    微信扫一扫登录
    下载资源需要3金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP,免费下载
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    第5章 中断和异常Word下载.docx

    1、第一级(称主片)的第二个中断请求输入端,与第二级8259A(称从片)的中断输出端INT相连,如图5.1所示。我们把与中断控制器相连的每条线叫做中断线,要使用中断线,就要进行中断线的申请,也就是IRQ(Interrupt ReQuirement ),因此我们也常把申请一条中断线称为申请一个IRQ或者是申请一个中断号。IRQ线是从0开始顺序编号的;因此,第一条IRQ线通常表示成IRQ0。IRQn的缺省向量是n+32;如前所述,IRQ和向量之间的映射可以通过中断控制器端口来修改。图5.1 级连的 8259A的中断机构并不是每个设备都可以向中断线上发中断信号的,只有对某一条确定的中断线拥有了控制权,才

    2、可以向这条中断线上发送信号。由于计算机的外部设备越来越多,所以15条中断线已经不够用了,中断线是非常宝贵的资源,所以只有当设备需要中断的时候才申请占用一个IRQ,或者是在申请IRQ时采用共享中断的方式,这样可以让更多的设备使用中断。 对于外部I/O请求的屏蔽可分为两种情况,一种是从CPU的角度,也就是清除eflag的中断标志位(IF),当IF=0时,禁止任何外部IO的中断请求,即关中断;一种是从中断控制器的角度,因为中断控制器中有一个8位的中断屏蔽寄存器,每位对应8259A中的一条中断线,如果要禁用某条中断线,则把中断屏蔽寄存器相应的位置1,要启用,则置0。5.1.3异常及非屏蔽中断 异常就是

    3、CPU内部出现的中断,也就是说,在CPU执行特定指令时出现的非法情况。非屏蔽中断就是计算机内部硬件出错时引起的异常情况。从上图可以看出,二者与外部I/O接口没有任何关系。Intel把非屏蔽中断作为异常的一种来处理,因此,后面所提到的异常也包括了非屏蔽中断。在CPU执行一个异常处理程序时,就不再为其他异常或可屏蔽中断请求服务,也就是说,当某个异常被响应后,CPU清除eflag的中IF位,禁止任何可屏蔽中断。但如果又有异常产生,则由CPU锁存(CPU具有缓冲异常的能力),待这个异常处理完后,才响应被锁存的异常。我们这里讨论的异常中断向量在031之间,不包括系统调用(中断向量为0x80)。 Inte

    4、l x86处理器发布了大约20种异常(具体数字与处理器模式有关)。Linux内核必须为每种异常提供一个专门的异常处理程序。5.1.4中断描述符表在实地址模式中,CPU把内存中从0开始的1K字节作为一个中断向量表。表中的每个表项占四个字节,由两个字节的段地址和两个字节的偏移量组成,这样构成的地址便是相应中断处理程序的入口地址。但是,在保护模式下,由四字节的表项构成的中断向量表显然满足不了要求。这是因为,除了两个字节的段描述符,偏移量必用四字节来表示;要有反映模式切换的信息。因此,在保护模式下,中断向量表中的表项由8个字节组成,如图5.2所示,中断向量表也改叫做中断描述符表IDT(Interrup

    5、t Descriptor Table)。其中的每个表项叫做一个门描述符(gate descriptor),“门”的含义是当中断发生时必须先通过这些门,然后才能进入相应的处理程序。 图5.2门描述符的一般格式其中类型占3位,表示门描述符的类型,主要门描述符为:(1)中断门(Interrupt gate)其类型码为110,中断门包含了一个中断或异常处理程序所在段的选择符和段内偏移量。当控制权通过中断门进入中断处理程序时,处理器清IF标志,即关中断,以避免嵌套中断的发生。中断门中的请求特权级(DPL)为0,因此,用户态的进程不能访问Intel的中断门。所有的中断处理程序都由中断门激活,并全部限制在内

    6、核态。(2)陷阱门(Trap gate)其类型码为111,与中断门类似,其唯一的区别是,控制权通过陷阱门进入处理程序时维持IF标志位不变,也就是说,不关中断。(3)系统门(System gate)这是Linux内核特别设置的,用来让用户态的进程访问Intel的陷阱门,因此,门描述符的DPL为3。系统调用就是通过系统门进入内核的。 最后,在保护模式下,中断描述符表在内存的位置不再限于从地址0开始的地方,而是可以放在内存的任何地方。为此,CPU中增设了一个中断描述符表寄存器IDTR,用来存放中断描述符表在内存的起始地址。中断描述符表寄存器IDTR是一个48位的寄存器,其低16位保存中断描述符表的大

    7、小,高32位保存中断描述符表的基址,如图5.3所示。 32位基址 界限 47 16 15 0 图5.3 中断描述符表寄存器IDTR 5.1.5 相关汇编指令为了有助于读者对中断实现过程的理解,下面介绍几条相关的汇编指令:(1) 调用过程指令CALL 指令格式: CALL 过程名 说明:在取出CALL指令之后及执行CALL指令之前,使指令指针寄存器EIP指向紧接CALL指令的下一条指令。CALL指令先将EIP值压入栈内,再进行控制转移。当遇到RET指令时,栈内信息可使控制权直接回到CALL的下一条指令(2) 调用中断过程的指令INTINT 中断向量EFLAG、CS及EIP寄存器被压入栈内。控制权

    8、被转移到由中断向量指定的中断处理程序。在中断处理程序结束时,IRET指令又把控制权送回到刚才执行被中断的地方。(3) 中断返回指令IRETIRETIRET与中断调用过程相反:它将EIP、CS及EFLAGS寄存器内容从栈中弹出,并将控制权返回到发生中断的地方。IRET用在中断处理程序的结束处。(4) 加载中断描述符表的指令LIDT 格式:LIDT 48位的伪描述符LIDT将指令中给定的48位伪描述符装入中断描述符寄存器IDTR。5.2中断描述符表的初始化 通过上面的介绍,我们知道了Intel微处理器对中断和异常所做的工作。下面,我们从操作系统的角度来对中断描述符表的初始化给予描述。 Linux内

    9、核在系统的初始化阶段要进行大量的初始化工作,其与中断相关的工作有:初始化可编程控制器8259A;将中断描述符表的起始地址装入IDTR寄存器,并初始化表中的每一项。这些操作的完成将在本节进行具体描述。 用户进程可以通过INT指令发出一个中断请求,其中断请求向量在0255之间。为了防止用户使用INT指令模拟非法的中断和异常,必须对中断描述符表进行谨慎的初始化。其措施之一就是将中断门或陷阱门中的请求特权级DPL域置为0。如果用户进程确实发出了这样一个中断请求,CPU会检查出其当前特权级CPL(3)与所请求的特权级DPL(0)有冲突,因此产生一个“通用保护”异常。 但是,有时候必须让用户进程能够使用内

    10、核所提供的功能(比如系统调用),也就是说从用户态进入内核态,这可以通过把中断门或陷阱门的DPL域置为3 来达到。 当计算机运行在实模式时,中断描述符表被初始化,并由BIOS使用。然而,一旦真正进入了Linux内核,中断描述符表就被移到内存的另一个区域,并为进入保护模式进行预初始化:用汇编指令LIDT对中断向量表寄存器IDTR进行初始化,即把IDTR置为0。把中断描述符表IDT的起始地址装入IDTR用setup_idt()函数填充中断描述表中的256个表项。在对这个表进行填充时,使用了一个空的中断处理程序。因为现在处于初始化阶段,还没有任何中断处理程序,因此,用这个空的中断处理程序填充每个表项。

    11、在对中断描述符表进行预初始化后, 内核将在启用分页功能后对IDT进行第二遍初始化,也就是说,用实际的陷阱和中断处理程序替换这个空的处理程序。一旦这个过程完成,对于每个异常,IDT都由一个专门的陷阱门或系统门,而对每个外部中断,IDT都包含专门的中断门。5.2.1 IDT表项的设置IDT表项的设置是通过_set_gaet()函数实现的,在此,我们给出如何调用该函数在IDT表中插入一个门: (1) 插入一个中断门void set_intr_gate(unsigned int n, void *addr) _set_gate(idt_table+n,14,0,addr);其中,idt_table是中

    12、断描述符表IDT在程序中的符号表示, n表示在第n个表项中插入一个中断门。这个门的段选择符设置成代码段的选择符,DPL域设置成0,14表示D标志位为1(表示32位)而类型码为110,所以set_intr_gate()设置的是中断门,偏移域设置成中断处理程序的地址addr。 (2) 插入一个陷阱门static void _init set_trap_gate(unsigned int n, void *addr) _set_gate(idt_table+n,15,0,addr); 在第 n个表项中插入一个陷阱门。这个门的段选择符设置成代码段的选择符,DPL域设置成0,15表示D标志位为1而类型码

    13、为111,所以set_trap_gate()设置的是陷阱门,偏移域设置成异常处理程序的地址addr。 (3) 插入一个系统门static void _init set_system_gate(unsigned int n, void *addr) _set_gate(idt_table+n,15,3,addr);在第 n个表项中插入一个系统门。这个门的段选择符设置成代码段的选择符,DPL域设置成3,15表示D标志位为1而类型码为111,所以set_system_gate()设置的也是陷阱门,但因为DPL为3,因此,系统调用在用户态可以通过“INT 0x80”顺利穿过系统门,从而进入内核态。5.

    14、2.2对陷阱门和系统门的初始化trap_init()函数就是设置中断描述符表开头的19个陷阱门和系统门,这些中断向量都是CPU保留用于异常处理的: set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call);其中,“&”之后的名字就是每个异常处理程序的名字。最后一个是对系统调用的设置。5.2.3 中断门的设置中断门的设置是由init_IRQ( )函数中的一段代码完成的

    15、:for (i = 0; i = NR_IRQS) break; if (vector != SYSCALL_VECTOR) set_intr_gate(vector, interrupti); 从FIRST_EXTERNAL_VECTOR开始,设置NR_IRQS(NR_VECTORS - FIRST_EXTERNAL_VECTOR)个IDT表项。常数FIRST_EXTERNAL_VECTOR定义为0x20,而NR_IRQS则为224,即中断门的个数。注意,必须跳过用于系统调用的向量0x80,因为这在前面已经设置好了。 这里,中断处理程序的入口地址是一个数组interrupt,数组中的每个元素

    16、是指向中断处理函数的指针。5.2.4 中断处理程序的形成 由前一节知道,interrupt为中断处理程序的入口地址,这只是一个笼统的说法。实际上不同的中断处理程序,不仅名字不同,其内容也不同,但是,这些函数又有很多相同之处,因此应当以统一的方式形成其函数名和函数体,于是,内核对该数组的定义如下: static void (*interruptNR_VECTORS - FIRST_EXTERNAL_VECTOR)(void) = IRQLIST_16(0x2), IRQLIST_16(0x3), IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6),

    17、 IRQLIST_16(0x7),IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf); 这里定义的数组interrupt,从IRQLIST_16(0x2)到IRQLIST_16(0xf)一共有14个数组元素,其中IRQLIST_16()宏的定义如下: #define IRQLIST_16(x) IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), IRQ

    18、(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)该宏中定义了16个IRQ(x,y),这样就有224(14*16)个函数指针。不妨再接着展开IRQ(x,y)宏: #define IRQ(x,y) IRQ#x#y#_interrupt# 表示将字符串连接起来,比如IRQ(0x2,0)就是IRQ0x20_interrupt。综上可知,以这样的方式就定义出224个函数,从 IRQ0x20_interrupt一直到IRQ0x

    19、ff_interupt。那么这些函数名又是如何形成的?我们看如下宏定义: #define IRQ_NAME2(nr) nr#_interrupt(void) #define IRQ_NAME(nr) IRQ_NAME2(IRQ#nr)从这两个宏的定义可以推知,IRQ_NAME(n)就是IRQn_interrupt(void)函数形式,其中随n具体数字不同,则形成不同的IRQn_interrupt()函数名。接下来,又如何以统一的方式让这些函数拥有内容,也就是说,这些函数的代码是如何形成的?内核定义了BUILD_IRQ宏。BUILD_IRQ宏是一段嵌入式汇编代码,为了有助于理解,我们把它展开成下

    20、面的汇编语言片段:IRQn_interrupt:pushl$n-256jmpcommon_interrupt把中断号减256的结果保存在栈中,这是进入中断处理程序后第一个压入堆栈的值,是一个负数,正数留给系统调用使用。对于每个中断处理程序,唯一不同的就是压入栈中的这个数。然后,所有的中断处理程序都跳到一段相同的代码common_interrupt。关于这段代码,请参看5.3.3一节中断处理程序IRQn_interrupt。5.3 中断处理 通过上面的介绍,我们知道了中断描述符表已被初始化,并具有了相应的内容;对于外部中断,还要建立中断请求队列,以及执行中断处理程序,这正是我们本节要关心的主要内

    21、容。5.3.1中断和异常的硬件处理。 首先,我们从硬件的角度来看CPU如何处理中断和异常。这里假定内核已被初始化,CPU已从实模式转到保护模式。 当CPU执行了当前指令之后,CS和EIP这对寄存器中所包含的内容就是下一条将要执行指令的虚地址。在对下一条指令执行前,CPU先要判断在执行当前指令的过程中是否发生了中断或异常。如果发生了一个中断或异常,那么CPU将做以下事情:(1) 确定所发生中断或异常的向量i(在0255之间)。(2) 通过IDTR寄存器找到IDT表,读取IDT表第i项(或叫第i个门)。(3) 分两步进行有效性检查:首先是“段”级检查,将CPU的当前特权级CPL(存放在CS寄存器的

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

    23、说,如果产生了中断或异常,就免去了“门”级检查。(4) 检查是否发生了特权级的变化。当中断发生在用户态(特权级为3),而中断处理程序运行在内核态(特权级为0),特权级发生了变化,所以会引起堆栈的更换。也就是说,从用户堆栈切换到内核堆栈。而当中断发生在内核态时,即CPU在内核中运行时,则不会更换堆栈,如图5.4所示。错误码 中断处理程序的堆栈特权级发生了变化 特权级没有变化图5.4中断处理程序堆栈示意图从图可以看出,当从用户态堆栈切换到内核态堆栈时,先把用户态堆栈的值压入中断程序的内核态堆栈中,同时把 EFLAGS寄存器自动压栈,然后把被中断进程的返回地址压入堆栈。如果异常产生了一个硬错误码,则

    24、将它也保存在堆栈中。如果特权级没有发生变化,则压入栈中的内容如图5.4中。此时,CS:EIP的值就是IDT表中第i项门描述符的段选择符和偏移量的值,于是,CPU就跳转到了中断或异常处理程序。5.3.2 中断请求队列的建立由于硬件的限制,很多外部设备不得不共享中断线,例如,一些PC配置可以把同一条中断线分配给网卡和图形卡。由此看来,让每个中断源都必须占用一条中断线是不现实的。所以,仅仅中断描述符表并不能提供中断产生的所有信息,内核必须对中断线给出进一步的描述。在Linux设计中,专门为每个中断请求IRQ设置了一个队列,这就是所谓的中断请求队列。1中断服务程序与中断处理程序我们这里提到的中断服务程

    25、序ISR(Interrupt Service Routine)与以前所提到的中断处理程序(Interrupt handler)是两个不同的概念。在Linux中,15条中断线对应15个中断处理程序,其名依次为IRQ0x00_interrupt(),IRQ0x01_interrupt()IRQ0x0f_interrupt()。具体来说,中断处理程序相当于某个中断向量的总处理程序,例如IRQ0x05_interrupt()是中断号5(向量为37)的总处理程序,如果这个5号中断由网卡和图形卡共享,则网卡和图形卡分别有其相应的中断服务程序。2中断线共享的数据结构为了让多个设备能共享一条中断线而内核设置了

    26、一个叫irqaction的数据结构:typedef irqreturn_t (*irq_handler_t)(int, void *); struct irqaction irq_handler_t handler; unsigned long flags; cpumask_t mask; const char *name; void *dev_id; struct irqaction *next; int irq; ;对每个域描述如下:handler 指向一个具体I/O设备的中断服务程序,该函数有两个参数,第一个参数为中断号IRQ,第二个参数为void指针,该指针一般传入dev_id(唯一地

    27、标示某个设备的设备号)的值。flags 用一组标志描述中断线与I/O设备之间的关系。IRQF_DISABLED 中断处理程序执行时必须禁止中断IRQF_SHARED 允许其它设备共享这条中断线。IRQF_SAMPLE_RANDOM 可以把这个设备看作是随机事件发生源;因此,内核可以用它做随机数产生器。nameI/O设备名dev_id 指定I/O设备的主设备号和次设备号(参见第9章)。next指向irqaction描述符链表的下一个元素,前提是flags为IRQF_SHARED标志。共享同一中断线的每个硬件设备都有其对应的中断服务程序,链表中的每个元素就是对相应设备及中断服务程序的描述。3 注册

    28、中断服务程序在IDT表初始化完成之初,每个中断服务队列还为空。此时,即使打开中断且某个外设中断真的发生了,也得不到实际的服务。因为CPU虽然通过中断门进入了某个中断向量的总处理程序,例如IRQ0x05_interrupt(),但是,具体的中断服务程序(如图形卡的)还没有挂入中断请求队列。因此,在设备驱动程序的初始化阶段,必须通过request_irq()函数将相应的中断服务程序挂入中断请求队列,也就是对其进行注册。request_irq()函数原型为:int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqfla


    注意事项

    本文(第5章 中断和异常Word下载.docx)为本站会员主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

    copyright@ 2008-2023 冰点文库 网站版权所有

    经营许可证编号:鄂ICP备19020893号-2


    收起
    展开