WindowsLinux中断、异常处理机制分析.doc_第1页
WindowsLinux中断、异常处理机制分析.doc_第2页
WindowsLinux中断、异常处理机制分析.doc_第3页
WindowsLinux中断、异常处理机制分析.doc_第4页
WindowsLinux中断、异常处理机制分析.doc_第5页
免费预览已结束,剩余4页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

一、引言处理器的速度跟外围硬件设备的速度往往不在一个数量级上,因此,如果内核采取让处理器向硬件发出一个请求,然后专门等待回应的办法,显然差强人意。既然硬件的响应这么慢,那么内核就应该在此期间处理其他事务,等到硬件真正完成了请求的操作之后,再回过头来对它进行处理。想要实现这种功能,轮询(polling)可能会是一种解决办法。可以让内核定期对设备的状态进行查询,然后做出相应的处理。不过这种方法很可能会让内核做不少无用功,因为无论硬件设备是正在忙碌着完成任务还是已经大功告成,轮询总会周期性地重复执行。更好的办法是由我们来提供一种机制,让硬件在需要的时候再向内核发出信号(变内核主动为硬件主动),这就是中断机制。众所周知,在C+中有着较为完善的异常处理机制,同样在C语言中也有很不错的异常处理机制来支持,另外在其它许多现代编程语言中,也都有各自的异常处理编程机制,如Ada语言等。但我们更应该知道这些编程语言所提供的异常处理机制的实现,都是建立在操作系统中所提供的异常处理机制之上,如Windows平台上的VC编译器所实现的C+异常处理模型,它就是建立在SEH机制之上的。如果没有采用这种方式,比如Linux操作系统上的gcc就没有采用到操作系统中所提供的异常处理机制,这样就会有一个很大的缺点,那就是对于应用程序的开发者而言,它不能够很好在自己的应用程序中,来有效控制操作系统中所出现的一些意外的系统异常,例如程序执行过程中可能出现的段错误,被零除等计算异常,以及其它许多不同类型的系统异常等。所以Linux操作系统上的gcc编译的程序中,它只能捕获程序中,曾经被自己显式地抛出来的异常,而对于系统异常,catch block则毫无办法。因此,操作系统平台中所提供的中断、异常处理机制是非常有必要的。它除了能够帮助开发人员发现和解决软件中的错误外,还被广泛应用于软件保护技术、软件漏洞利用等方面。因此,深入研究中断、异常处理机制的原理和实现以扩展其应用的范围是有必要的。二、中断处理方式、WINDOWS系统的中断处理方式Windows内核对于中断使用了陷阱派发机制,它使用中断陷阱处理器来响应设备的中断,中断陷阱处理器或者将控制权给负责处理中断的外部例程(中断服务程序,ISR),或者传递给一个响应该中断的内部内核例程。设备驱动程序提供了ISR来处理设备中断,内核为其他类型的中断提供了ISR。Windows的中断主要有硬件中断和软件中断,通过Windows核心我们可以禁止软件中断和硬件中断。软件中断主要引发对线程的调度以及以异步方式打断一个线程执行的机制。而硬件中断主要是为了实现处理器和设备并行工作。对于硬件中断,Windows 将硬件中断转化为能够触发执行的驻留在不同虚拟机中的ISR(中断服务程序)事件,这是一个较好的中断处理方法。下面我们来讨论一下Windows系统的硬件终端处理机制。在Windows系统下,处理器可以在不同的状态(V86、实模式和保护模式)下运行。当DOS运行时,则处理器运行V86模式。当Windows 执行时或当DOS VM已经切换进保护模式时,处理器则运行Ring3保护模式。当VMM(虚拟机管理器)或VXD(虚拟设备驱动程序)执行时,处理器则运行在Ring0 保护模式。在Windows系统环境中,Windows将所有的IDT入口指向VMM 中的一个函数。VMM会判断出来自IDT入口项的调用是作为异常被调用还是作为中断被调用。VMM本身负责处理异常而将所有硬件中断交给一个名为VPICD(虚拟可编程中断控制器设备)的VXD。如果某个VXD 已经为某个硬件中断注册了中断处理函数,那么VPICD 就将中断传递给该VXD。如果没有,VPICD 将把某个中断传递给某个VM,这一过程被称作“中断反射”。VXD通过调用VPICD的VPICD_Viutualize_IRQL 服务函数为特定的硬件中断注册并将回调函数传递给VPICD。一旦VXD 已经为中断注册,它将作为一个真正的中断处理器,为中断设备进行中断服务,同时VXD 可能使用另一个VPICD 服务函数VPICD_Set_Int_Request来把中断映射到VM,让VM 的中断处理函数来提供服务。从IDT 到VXD 中断处理函数的途径在使用中断服务时,我们也要对下列情况进行注意。1、中断响应时间:为了实现实时操作,一般要求中断响应时间尽可能的短。由于硬件中断的响应过程比较复杂,中断响应时间通常在1ms 以上。为了使中断响应时间最短,硬件中断的处理应在VXD 中进行。但即使在VXD 中进行,VXD 也不能保证对硬件中断的实时响应。原因在于ring 转换以及VMM 和VPICD之间存在多个层次的联系。2、中断结束处理(EOI):在编写中断处理函数时,常见的错误是忘记EOI,导致一个硬件中断仅被调用一次。虽然设备本身能产生更多的中断,但是PIC(可编程中断控制器)不让这些中断到达处理器,并会一直持续到PIC 接受到EOI 为止。Windows 使用与DOS 不同的中断控制器的EOI机制。VPICD 是被中断通知的第一个VXD,然后VPICD 立即发送“特定EOI” 到控制器。然后VPICD 屏蔽控制器上的中断级别。这两个操作使得其他的中断级别可以被识别,包括那些比正在中断的级别低的优先权。当VXD 在退出中断处理函数前调用VPICD_Phys_EOI 服务函数时,VPICD 将清除相同级别上的中断的屏蔽。3、内存管理:处理硬件中断的驱动程序对所有分配的内存有严格的要求。所有在中断期间要访问的代码和数据都必须是固定的、页码锁定的和不可废弃的。这包括中断处理函数本身的代码以及在中断处理过程中要用到的驱动程序代码段、任何动态分配的缓冲区和应用程序分配的要传递给驱动程序的缓冲区。内存要求必须是固定的。因为内存管理器把中断处理函数要用到的数据段移动了,在一些字节被拷贝后,硬件中断发生,这时硬件中断处理函数被执行。硬件中断处理函数会更新正在移动的数据段中的一些内容,当处理函数结束运行之后,内存管理器接着移动数据段,而这时的数据段已经被处理函数更改过了。内存管理器并不知道处理函数已经改变了数据。而使用该数据段的应用程序就得不到希望得到的数据。同时内存又要求必须是页面锁定的。假设硬件中断发生了,而内存管理器是在可废弃的代码中,那么就会发生段不在内存中的警告,在此种情况下就有可能发生重入DOS 的情况,而DOS 代码是不可重入的,所以代码段是不可废弃的。、LINUX系统的中断处理方式当一个中断发生时,并不是所有的操作都具有相同的急迫性。事实上,把所有的操作都放进中断处理程序本身并不合适。需要时间长的、非重要的操作应该推后,因为当一个中断处理程序正在运行时,相应的IRQ 中断线上再发出的信号就会被忽略。另外中断处理程序不能执行任何阻塞过程,如I/O 设备操作。因此,Linux 把一个中断要执行的操作分为下面的三类:1、紧急的(Critical)这样的操作诸如:中断到来时中断控制器做出应答,对中断控制器或设备控制器重新编程,或者对设备和处理器同时访问的数据结构进行修改。这些操作都是紧急的,应该被很快地执行,也就是说,紧急操作应该在一个中断处理程序内立即执行,而且是在禁用中断的状态下。2、非紧急的(Noncritical)这样的操作诸如:修改那些只有处理器才会访问的数据结构(例如,按下一个键后,读扫描码)。这些操作也要很快地完成,因此,它们由中断处理程序立即执行,但在启用中断的状态下。3、非紧急可延迟的(Noncritical deferrable)这样的操作诸如:把一个缓冲区的内容拷贝到一些进程的地址空间(例如,把键盘行缓冲区的内容发送到终端处理程序的进程)。这些操作可能被延迟较长的时间间隔而不影响内核操作,有兴趣的进程会等待需要的数据。尽管分为上述的三种,但所有的中断处理程序都执行四个基本的操作:1、在内核栈中保存IRQ 的值和寄存器的内容;2、给与IRQ 中断线相连的中断控制器发送一个应答,这将允许在这条中断线上进一步发出中断请求;3、执行共享这个IRQ 的所有设备的中断服务例程(ISR);4、跳到ret_to_usr( )的地址后终止。Linux系统的中断主要有3种组织形式:1、IRQ 描述符irq_desc:对于每个IRQ 中断线,Linux 都用一个irq_desc_t 数据结构来描述,我们把它叫做IRQ 描述符,NR_IRQS 个IRQ 形成一个全局数组irq_desc,其定义在/include/linux/irq.h 中。2、中断控制器描述符irq_chip:由于CPU 不同,故每个处理器对于中断的处理方式不一样。Linux 为了实现统一的中断处理,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。3、中断服务例程描述符irqaction:在IRQ 描述符中我们看到指针action 的结构为irqaction,它是为多个设备能共享一条中断线而设置的一个数据结构,代表了每个注册中断对应的信息,其在include/linux/interrupt.h 中定义。下面我们来具体讨论一下中断过程。当外设的驱动程序都已完成了初始化工作,并且已把相应的中断服务例程挂入到特定的中断请求队列,同时当前进程正在用户空间运行(随时可以接受中断),且外设已产生了一次中断请求,CPU 就在执行完当前指令后来响应该中断。中断处理系统在 Linux 中的实现是非常依赖于体系结构的,实现依赖于处理器、所使用的中断控制器的类型、体系结构的设计及机器本身。设备产生中断,通过总线把电信号发送给中断控制器。如果中断线是激活的,那么中断控制器就会把中断发往处理器。在大多数体系结构中,这个工作就是通过电信号给处理器的特定管脚发送一个信号。除非在处理器上禁止该中断,否则,处理器会立即停止它正在做的事,关闭中断系统,然后跳到内存中预定义的位置开始执行那里的代码。这个预定义的位置是由内核设置的,是中断处理程序的入口点。对于 ARM 系统来说,有个专用的IRQ 运行模式,有一个统一的入口地址。假定中断发生时CPU 运行在用户空间,而中断处理程序属于内核空间,因此,要进行堆栈的切换。也就是说,CPU 从TSS 中取出内核栈指针,并切换到内核栈(此时栈还为空)。对于在Linux系统,如果当前处于内核空间时,对于 ARM 系统来说是处于SVC 模式,此时产生中断,中断处理完毕后,若是可剥夺内核,则检查是否需要进行进程调度,否则直接返回到被中断的内核空间;若需要进行进程调度,则svc_preempt,进程切换。如果当前处于用户空间时,对于 ARM 系统来说是处于USR 模式,此时产生中断,中断处理完毕后,无论是否是可剥夺内核,都调转到统一的用户模式出口ret_to_user,其检查是否需要进行进程调度,若需要进行进程调度,则进程切换,否则直接返回到被中断的用户空间。三、异常处理方式、WINDOWS系统的异常处理方式目前Windows 平台下实现和使用的异常处理机制主要有4 种:筛选器异常处理,结构化异常处理(Structure Exception Handler, SEH),向量化异常处理(Vectored Exception Handler,VEH),C+异常处理(C+ Exception Handler, C+EH)。前3 种是由Windows 操作系统实现的异常处理机制,C+EH 是由C+编译器实现的异常处理机制,但它们在其内部实现机理和调用底层函数方面是十分相似的。1、筛选器异常处理筛选器异常处理方式,它采用的是在进程范围内注册一个异常处理回调函数,并返回前一个注册的异常处理回调函数的句柄。注册函数如下:LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);其中的参数表示回调函数的地址。当结构化异常SEH无法处理的时候,系统就会调用筛选器异常处理。当筛选器异常处理也无法搞定的时候,系统会调用系统默认的异常处理程序。筛选器异常处理是属于整个进程的,即是“全局”的,这一点跟SEH不同。2、 结构化异常处理(SEH)下面是出自Window核心编程中一小段内容的引用:“微软在Windows中引入SEH的主要动机是为了便于操作系统本身的开发。操作系统的开发人员使用SEH,使得系统更加强壮。我们也可以使用SEH,使我们的自己的程序更加强壮。使用SEH所造成的负担主要由编译程序来承担,而不是由操作系统承担。当异常块(exception block)出现时,编译程序要生成特殊的代码。编译程序必须产生一些表(table)来支持处理SEH的数据结构。编译程序还必须提供回调(callback)函数,操作系统可以调用这些函数,保证异常块被处理。编译程序还要负责准备栈结构和其他内部信息,供操作系统使用和参考。在编译程序中增加SEH支持不是一件容易的事。不同的编译程序厂商会以不同的方式实现SEH,这一点并不让人感到奇怪。”可见,SEH 是Windows 平台下使用最早和最广泛的异常处理机制。它最基本的特点是在堆栈中实现并且分为进程相关和线程相关种类型。下面分别对用户模式下的原始型SEH和封装型SEH 进行讨论。、原始型SEHSEH 的进程相关类型是整个进程作用范围的异常处理函数,通过WIN32的API函数SetUnhandledExceptionFilter进行注册,而操作系统内部使用一个全局变量来记录这个顶层的处理函数,因此只能有一个全局性的异常处理函数。而线程相关类型的作用范围是本线程内,并且可注册多个,甚至可以嵌套注册。两者相比,线程相关类型在实际应用中使用较为广泛。当线程初始化时,会自动向栈中安装一个异常处理结构,作为线程默认的异常处理。SEH 最基本的数据结构是保存在堆栈中的称为EXCEPTION_REGISTRATION 的结构体,结构体包括2个元素:第1个元素是指向下一个EXCEPTION_REGISTRATION 结构的指针(prev),第2个元素是指向异常处理程序的指针(handler)。这样一来,基于堆栈的异常处理程序就相互连接成一个链表。异常处理结构在堆栈中的典型分布如图 所示。最顶端的异常处理结构通过线程控制块(TEB)0 Byte 偏移处指针标识,即FS:0处地址。用于进行实际异常处理的函数原型可表示如下:EXCEPTION_DISPOSITION _cdecl _except_handler(struct _EXCEPTION_RECORD *ExceptionRecord,void * EstablisherFrame,struct _CONTEXT *ContextRecord,void * DispatcherContext)该函数的最重要的2个参数是指向_EXCEPTION_RECORD 结构的ExceptionRecord 参数和指向_CONTEXT 结构的ContextRecord 参数,前者主要包括异常类别编码、异常发生地址等重要信息;后者主要包括异常发生时的通用寄存器、调试寄存器和指令寄存器的值等重要的线程执行环境。而用于注册异常处理函数的典型汇编代码可表示如下:PUSH handler ; PUSH FS:0 ; MOV FS:0,ESP ; 当异常发生时,操作系统的异常分发函数在进行初始处理后,如果异常没有被处理就会开始在上图所示的线程堆栈上遍历异常处理链,直到异常被处理,如果仍没有注册函数处理异常,则将异常交给缺省处理函数或直接结束产生异常的进程。、封装型SEH 通过使用_try/_except()/_finally等关键字,使开发人员更方便地在软件中使用SEH 是封装型SEH 的主要特点。该机制的异常处理数据结构定义如下:struct VC_EXCEPTION_REGISTRATION VC_EXCEPTION_REGISTRATION* prev;FARPROC handler;scopetable_entry* scopetable; int _index;DWORD _ebp; 其中,结构体中后3 个成员是新增的。而scopetable_entry 的结构如下所示:struct scopetable_entry DWORD prev_entryindex; FARPROC lpfnFilter; FARPROC lpfnHandler; 封装型SEH 的基本思想是为每个函数内的_try块建立一scopetable表,每个_try块对应于scopetable中的一项,该项指向_try块对应的scopetable_entry 结构,该结构含有与_except()/_finally对应的过滤函数和处理函数。若有_try块嵌套,则在scopetable_entry 结构的prev_entryindex成员中指明,多层嵌套形成单向链表。而每个函数只注册一个VC_EXCEPTION_REGISTRATION 结构, 该结构中的handler 成员是一个重要的运行时库函数_except_handler3。该异常处理回调函数负责对结构中的成员进行设置,查找处理函数并根据处理结果决定是继续执行还是让系统继续遍历外层SEH 链。说到底,封装型SHE 的内部机理,它只是扩展了原始型SEH 的功能,使用它可以简化软件开发人员的工作。3、向量化异常处理(VEH)向量化异常处理(VEH)是在Windows XP 以后版本中新增的一种异常处理机制。通过使用Win32 API 函数AddVectoredExceptionHandler 就可以注册新的异常处理函数,函数的参数就是指向EXCEPTION_POINTERS 结构的指针。而VEH 用到的数据结构可以构成一个双向链表。通过对用于注册VEH 的函数进行反汇编分析研究可知,VEH 数据结构的相关信息存储在堆中。在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历VEH 记录链表的函数,如果没有找到可以处理异常的注册函数,再开始遍历SEH 注册链表。通过对 VEH 的原理和实现的研究,可以总结出VEH 具有如下的特点:(1)VEH 是进程相关的,同时可以注册多个,甚至嵌套注册,而且注册的位置可以指定。(2)VEH 保存在堆中,而不像SEH 保存在堆栈中。(3)VEH 只能用在用户模式程序中,而SEH 还可以用在内核模式中。(4)VEH 处理先于SEH 处理执行,只有所有VEH全不处理某个异常的时候,异常处理权才会到达SEH。正因为 VEH 具有以上特点,所以在实际应用中它的使用更加灵活,甚至可以利用它来实现一个微型调试器。4、C+异常处理(C+EH)与SHE, VEH 相比,C+异常处理的内部实现更加复杂,因为它牵扯到类、对象等相关处理,但C+EH 也是建立在基本异常处理机制基础之上的。另外,编译器提供了类似封装型SEH 的关键字try/catch()/throw 帮助开发人员方便地处理C+程序中出现的各种异常。C+EH 的独有特点有:首先,C+EH 扩展了EXCEPTION_REGISTRATION 结构,新添加的成员主要作用是当异常发生时用于确定对应的try块,功能类似于封装型SEH 结构中的_index 成员。当编译器对带有C+EH 的函数进行编译时,要添加2 个重的数据结构: 一个是异常处理函数_CxxThrowException();另一个是funcinfo 结构,里面包含对应catch块的地址、catch块所关心的异常类型等重要的信息。_CxxThrowException()是C+EH 的统一的异常处理函数,相当于封装型SEH 中的_except_handler3()。_CxxThrowException()函数内部还是调用了Win32API 函数RaiseException()来产生异常,并且异常码是固定的0xE06D7363。当 C+异常发生时,发生异常函数的funcinfo 结构传给_CxxThrowException(),开始寻找是否有对该异常感兴趣的catch块,如果有就调用catch块处理异常;如果没有则沿异常处理链表继续寻找,直到异常被处理或最后抛出错误对话框为止。C+EH 除了增加了funcinfo 结构外,还增加了tryblock, catchblock 等数据结构,主要的目的就是应对复杂的try/catch()/throw 结构,在异常发生时能正确找到对应的异常处理块,在发生堆栈展开时能够调用对象的析构函数,释放资源等。、LINUX系统的异常处理方式Linux系统把所有进程数据结构都放于内核,这就增加了一些不必要的切换时间。 Linux可以通过系统调用,安装信号的回调函数,这回调函数指针存放在内核的进程数据结构里面。这点Windows处理得比较好,Windows把进程数据结构分成了两部分,一部分敏感数据放于内核的进程数据结构里面,加以保护,另一部分不敏感数据就放于用户空间,这样当访问那些不加保护的数据时,就不用切换到内核,节约了时间。像Windows下异常处理,也是一种回调函数,但因为结构放于用户空间,安装的时候就很方便,也节约切换时间。除了上述的效率问题,Linux内核并不支持类似Windows下的SHE,当Linux发生异常,会自动产生一个异常中断。在这异常中断处理程序中会判断异常来自用户程序或者内核,如果是发生在用户程序,那么会产生一个异常信号,再根据异常信号的回调函数通知用户程序发生异常。如果发生在内核里面,那么就会搜索内核模块的异常结构表,找到相应的处理调用地址,修改异常中断的返回地址为异常处理的地址,中断返回的时候程序就跳到异常处理程序处理执行。LInux有个异常模块表,保存会发生异常时候的eip和异常处理程序指针,发生异常的时候就根据异常时候的eip搜索表里面的eip,发现相等就找到了异常处理指针。这就意味着编写的内核程序必须精确的知道哪条指令可能会发生异常,这样的话要求就会很高。这就不如Windows下的异常编程那么轻松,程序员只需要知道哪一段程序可能出现异常,就只需要一个括号一个异常语句保护这段程序就可以了。可见异常表对于Linux是多么重要,虽然对于我们的编程来说Linux的异常处理方式较Windows处于下风,但它还是很实用的,下面我们来讨论一下利用异常表处理 Linux 内核态缺页异常的方式。在程序的执行过程中,因为遇到某种障碍而使 CPU 无法最终访问到相应的物理内存单元,即无法完成从虚拟地址到物理地址映射的时候,CPU 会产生一次缺页异常,从而进行相应的缺页异常处理。基于 CPU 的这一特性,Linux 采用了请求调页(Demand Paging)和写时复制(Copy On Write)的技术。请求调页是一种动态内存分配技术,它把页框的分配推迟到不能再推迟为止。这种技术的动机是:进程开始运行的时候并不访问地址空间中的全部内容。事实上,有一部分地址也许永远也不会被进程所使用。程序的局部性原理也保证了在程序执行的每个阶段,真正使用的进程页只有一小部分,对于临时用不到的页,其所在的页框可以由其它进程使用。因此,请求分页技术增加了系统中的空闲页框的平均数,使内存得到了很好的利用。从另外一个角度来看,在不改变内存

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论