Windows内核源码详尽分析_第1页
Windows内核源码详尽分析_第2页
Windows内核源码详尽分析_第3页
Windows内核源码详尽分析_第4页
Windows内核源码详尽分析_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

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

文档简介

中断处理每个cpu有一张中断表,简称IDT。IDT的整体布局:【异常->空白->5系->硬】(推荐采用7字口诀的方式重点记忆)异常:前20个表项存放着各个异常的描述符(IDT表不仅可以放中断描述符,还放置了所有异常的异常处理描述符,0x00-0x13)保留:0x14-0x1F,忽略这块号段空白:接下来存放一组空闲的保留项(0x20-0x29),供系统和程序员自己分配注册使用5系:然后是系统自己注册的5个预定义的软中断向量(软中断指手动的INT指令)(0x2A-0x2E5个系统预注册的中断向量,0x2A:KiGetTickCount,0x2B:KiCallbaclReturn0x2C:KiRaiseAssertion,0x2D:KiDebugService,0x2E:KiSystemService)硬:最后的表项供驱动程序注册硬件中断使用和自定义注册其他软中断使用(0x30-0xFF)下面是中断号的具体的分配情况:0x00-0x13固定分配给异常:0x14-0x1f:Intel保留给他公司将来自己使用(OS和用户都不要试图去使用这个号段,不安全) 以下的号段可用于自由分配给OS、硬件、用户使用 linux等其他系统是怎么划分这块号段的,不管,我们只看Windows的情况0x20-0x29:Windows没占用,因此这块号段我们也可以自由使用0x2A-0x2E:Windows自己本身使用的5个中断号0x30-0xFF:Windows决定把这块剩余的号段让给硬件和用户使用参见《寒江独钓》一书P93页注册键盘中断时,搜索空闲未用表项是从0x20开始,到0x29结束的,就知道为什么寒江独钓是在这段范围内搜索空白表项了(其实我们也完全可以从0x14开始搜索)Windows系统中,0x30-0xFF这块号段让给了硬件和用户自己使用。事实上,这块号段的开头部分默认都是让给硬件IRQ使用的,也即是分配给硬件IRQ的。IRQN默认映射到中断号0x30+N,如IRQ0用于系统时钟,系统时钟中断号默认对应就是0x30。当然程序员也可以修改APIC(可编程中断控制器)将IRQ映射到自定义的中断号。IRQ对外部设备分配,但IRQ0,IRQ2,IRQ13必须如下分配:IRQ0 >间隔定时设备IRQ2 >8259A芯片IRQ13 >外部数学协处理器其余的IRQ可以任意分配给外部设备。虽然一个IRQ只对应一个中断号,但是由于IRQ数量有限,而设备种类成千上万,因此多个设备可以使用同一个IRQ,进而,多个设备可以分配同一个中断号。因此,一个中断号可以共享给多个设备同时使用。Pnp设备在插入系统后,相应的总线驱动会自动为其创建一个用作栈底基石的pdo,然后给这个pdo发出一个IRP_MN_QUERY_RESOURCE_REQUIREMENTS,查询得到初步的资源需求。然后,pnp管理器会找到相应的硬件端口驱动,调用其AddDevice函数,当这个函数返回后,该硬件设备的设备栈已经建立起立了,pnp管理器就给栈顶设备发出一个IRP_MN_FILTER_RESOURCE_REQUIREMENTS再次询问该硬件需要的资源(功能驱动此时可以拦截处理这个irp,修改资源需求),当确定好最终的资源需求后,系统就协调分配端口号、中断号、DIRQL等硬件资源给它。分配完后,就发出一个IRP_MN_START_DEVICE给栈顶设备请求启动该硬件设备。当该irp下发来到端口驱动(指真正的硬件驱动)时,端口驱动这时就需要在分配的中断号上注册一个中断服务例程,以处理硬件中断,与设备进行交互。下面的函数就是用来注册中断服务例程的(准确的说法叫‘挂接中断')NTSTATUSIoConnectInterrupt(OUTPKINTERRUPT*InterruptObject,//返回创建的中断对象(一般是一个数组)INPKSERVICE_ROUTINEServiceRoutine,//我们的isr(ourisr)INPVOIDServiceContext,//isr的参数INPKSPIN_LOCKSpinLock,//我们isr的自旋锁,用于多cpu互斥(一般传NULL即可)INULONGVector,//分配到的中断号INKIRQLIrql,//isr对应的irqlINKIRQLSynchronizeIrql,//必须〉=Irql,—般=Irql即可(isr实际运行在这个irql)INKINTERRUPT_MODEInterruptMode,//表示是否允许执行本中断的下一个中断INBOOLEANShareVector,//表示本中断对象是否想要共享中断号以及是否允许共享INKAFFINITYProcessorEnableMask,//本isr的cpu亲缘性,一般全部cpu都亲缘。INBOOLEANFloatingSave)//—般为FALSE{PKINTERRUPTInterrupt;PKINTERRUPTInterruptUsed;//当前的中断对象PIO_INTERRUPTIoInterrupt;//中断对象数组的头部PKSPIN_LOCKSpinLockUsed;//实际使用的自旋锁BOOLEANFirstRun;CCHARCount=0;//cpu号KAFFINITYAffinity;//cpu亲缘性掩码PAGED_CODE();*InterruptObject=NULL;Affinity=ProcessorEnableMask&KeActiveProcessors;//本isr的cpu亲缘性与实有cpu的交集while(Affinity){if(Affinity&1)Count++;Affinity>>=1;}//上面的循环根据本isr可以在哪些cpu上运行,得出可运行的cpu个数if(!Count)returnSTATUS_INVALID_PARAMETER;//分配一个中断对象数组IoInterrupt=ExAllocatePoolWithTag(NonPagedPool,(Count-1)*sizeof(KINTERRUPT)+sizeof(IO_INTERRUPT),TAG_KINTERRUPT);if(!IoInterrupt)returnSTATUS_INSUFFICIENT_RESOURCES;*InterruptObject=&IoInterrupt->FirstInterrupt;//if用户没提供自旋锁,就使用内置的自旋锁。一般用户不需自己提供自旋锁SpinLockUsed=SpinLock?SpinLock:&IoInterrupt->SpinLock;Interrupt=(PKINTERRUPT)(IoInterrupt+1);//后面的中断对象数组地址FirstRun=TRUE;RtlZeroMemory(IoInterrupt,sizeof(IO_INTERRUPT));Affinity=ProcessorEnableMask&KeActiveProcessors;for(Count=0;Affinity;Count++,Affinity>>=1)//Count其实表示cpu号{if(Affinity&1){//第一次使用头部中的那个内置中断对象InterruptUsed=FirstRun?&IoInterrupt->FirstInterrupt:Interrupt;//构造一个中断对象KeInitializeInterrupt(InterruptUsed,ServiceRoutine,ServiceContext,SpinLockUsed,Vector,Irql,SynchronizeIrql,InterruptMode,ShareVector,Count,FloatingSave);if(!KeConnectInterrupt(InterruptUsed))//关键,挂接中断对象到目标cpu的指定中断号{//if挂接失败if(FirstRun)ExFreePool(IoInterrupt);elseIoDisconnectInterrupt(&IoInterrupt->FirstInterrupt);returnSTATUS_INVALID_PARAMETER;}if(FirstRun)FirstRun=FALSE;Else//记录各cpu的那个中断号上挂接的中断对象地址IoInterrupt->Interrupt[(UCHAR)Count]=Interrupt++;}}returnSTATUS_SUCCESS;}如上,这个函数用来将指定isr挂接到各个cpu的指定中断号上。因为在多cpu系统中,一个设备可以向每个cpu都发出中断,因此,必须在每个cpu的IDT中都要挂接登记那个中断的isr。具体是怎么挂接的呢?这个函数会创建一个中断对象数组,然后将各个中断对象对应挂接到各cpu的同一中断号上。由于老式机器是单cpu的,因此,早期的中断对象结构IO_INTERRUPT就包含一个中断对象任意,后来的机器对其进行了扩展,在这个结构后面是一个中断对象数组,用来挂接到其他cpu上。另外,由于多个设备可以共用同一中断号,所以每个中断号需要一个自己的链表来记录所有挂接在此中断号上的所有中断对象。typedefstruct_IO_INTERRUPT{KINTERRUPTFirstlnterrupt;//内置的中断对象PKINTERRUPTInterrupt[MAXIMUM_PROCESSORS];//记录各cpu上挂接的中断对象地址KSPIN_LOCKSpinLock;//内置的isr自旋锁,如果用户没提供,就默认使用这个公共的自旋锁。}IO_INTERRUPT,*PIO_INTERRUPT;该结构体后面紧跟一个INTERRUPT结构体数组typedefstruct_KINTERRUPT//中断对象{CSHORTType;CSHORTSize;LIST_ENTRYInterruptListEntry;//用来挂入中断对象链表PKSERVICE_ROUTINEServiceRoutine;//我们的isr(用户的isr)PVOIDServiceContext;//isr参数KSPIN_LOCKSpinLock;//一般无用ULONGTickCount;//没用PKSPIN_LOCKActualLock;//我们isr实际使用的自旋锁PKINTERRUPT_ROUTINEDispatchAddress;//中间的Dispatchisr函数地址CCHARNumber;//要挂往的目标cpuULONGVector;//要挂往的目标中断号KIRQLIrql;//isr对应的isrKIRQLSynchronizelrql;//isr实际运行在的irqlBOOLEANFloatingSave;//—般为FALSEBOOLEANConnected;//表示本中断对象是否挂上去了BOOLEANShareVector;//是否想要共享中断号,以及是否允许后来的中断对象共享KINTERRUPT_MODEMode;//是否允许继续执行本中断对象后面的中断对象的isrULONGServiceCount;//没用ULONGDispatchCount;//没用ULONGDispatchCode[DISPATCH_LENGTH];//这不是数组,而是一段代码,表示本中断对象的模板isr}KINTERRUPT;下面的函数用来构造、初始化一个中断对象VOIDKeInitializeInterrupt(INPKINTERRUPTInterrupt,INPKSERVICE_ROUTINEServiceRoutine,INPVOIDServiceContext,INPKSPIN_LOCKSpinLock,INULONGVector,INKIRQLIrql,INKIRQLSynchronizeIrql,INKINTERRUPT_MODEInterruptMode,INBOOLEANShareVector,INCHARProcessorNumber,INBOOLEANFloatingSave){ULONGi;PULONGDispatchCode=&Interrupt-〉DispatchCode[0],Patch=DispatchCode;//patch表示补丁处Interrupt->Type=InterruptObject;Interrupt->Size=sizeof(KINTERRUPT);if(SpinLock)//由于这个函数未导出,由系统内部调用,传的SpinLock参数很少为NULLInterrupt->ActualLock=SpinLock;//使用头部中公共的自旋锁或者我们提供的自旋锁else{KeInitializeSpinLock(&Interrupt->SpinLock);Interrupt->ActualLock=&Interrupt->SpinLock;}Interrupt->ServiceRoutine=ServiceRoutine;Interrupt->ServiceContext=ServiceContext;Interrupt->Vector=Vector;Interrupt->Irql=Irql;Interrupt->SynchronizeIrql=SynchronizeIrql;Interrupt->Mode=InterruptMode;Interrupt->ShareVector=ShareVector;Interrupt->Number=ProcessorNumber;Interrupt->FloatingSave=FloatingSave;Interrupt->TickCount=MAXULONG;Interrupt->DispatchCount=MAXULONG;//先拷贝模板isr的字节码到中断对象内部for(i=0;i<DISPATCH_LENGTH;i++)*DispatchCode++=((PULONG)KiInterruptTemplate)[i];//patch指向模板isr中的movedx,0指令的操作数部分Patch=(PULONG)((ULONG)Patch+((ULONG)&KiInterruptTemplateObject-4-(ULONG)KiInterruptTemplate));*Patch=PtrToUlong(Interrupt);//也即将原movedx,0改为movedx,本中断对象的地址Interrupt-〉Connected=FALSE;//尚未挂入}下面是系统的模板isr:_KiInterruptTemplate:KiEnterTrapKI_PUSH_FAKE_ERROR_CODE_KiInterruptTemplate2ndDispatch:movedx,0//这条指令的操作数0将被动态修改成具体中断对象的地址_KiInterruptTemplateObject:moveax,offset@KiInterruptTemplateHandler@8//KilnterruptTemplateHandler函数jmpeax_KiInterruptTemplateDispatch:上面就是系统的模板isr,每个中断对象的模板isr就是从系统的模板isr复制过来的,然后稍作修改。当构造好中断对象后,就需要把它挂接到目标cpu的目标中断号上。下面的函数就这个用途BOOLEAN//返回值表示是否挂接成功KeConnectInterrupt(INPKINTERRUPTInterrupt){BOOLEANConnected,Error,Status;KIRQLIrql,OldIrql;UCHARNumber;ULONGVector;DISPATCH_INFODispatch;Number=Interrupt-〉Number;//目标cpuVector=Interrupt-〉Vector;//目标中断号Irql=Interrupt->Irql;//SynchronizeIrql必须>=Irqlif((Irql>HIGH_LEVEL)||(Number>=KeNumberProcessors)||(Interrupt->SynchronizeIrql<Irql)||(Interrupt->FloatingSave))returnFALSE;}Connected=FALSE;Error=FALSE;KeSetSystemAffinityThread(l<<Number);//改变当前线程的cpu亲缘性先,挪到目标cpu上去运行 华丽的分割线 //下面的这些代码已经处在目标cpu上运行了OldIrql=KiAcquireDispatcherLock();if(!Interrupt-〉Connected)//if尚未挂接{//查询当前cpu这个中断号上的最近一次(也即上一次)的挂接情况KiGetVectorDispatch(Vector,&Dispatch);if(Dispatch.Type==NoConnect)//如果这个中断号尚未挂接有任何中断对象{Interrupt->Connected=Connected=TRUE;InitializeListHead(&Interrupt-〉InterruptListEntry);//独立//NormalConnect表示以普通方式挂上去(非链接方式),相当于覆盖方式KiConnectVectorToInterrupt(Interrupt,NormalConnect);Status=HalEnableSystemInterrupt(Vector,Irql,Interrupt-〉Mode);//APIC相关if(!Status)Error=TRUE;}elseif((Dispatch.Type!=UnknownConnect)&&//已挂接有中断对象(Interrupt->ShareVector)&& //本中断对象想要共享这个中断号(Dispatch.Interrupt->ShareVector)&&//并且上次挂接的那个中断对象允许共享(Dispatch.Interrupt->Mode==Interrupt->Mode)){Interrupt->Connected=Connected=TRUE;//if上一个中断对象不是以链接方式挂上去的,就改为链接方式挂上去if(Dispatch.Type!=ChainConnect){ASSERT(Dispatch.Interrupt->Mode!=Latched);KiConnectVectorToInterrupt(Dispatch.Interrupt,ChainConnect);}//关键。挂在上一个中断对象的后面InsertTailList(&Dispatch.Interrupt->InterruptListEntry,&Interrupt->InterruptListEntry);}}KiReleaseDispatcherLock(OldIrql);KeRevertToUserAffinityThread();if((Connected)&&(Error)){KeDisconnectInterrupt(Interrupt);Connected=FALSE;}returnConnected;}下面的函数用于查询当前cpu指定中断号上的最近一次挂接情况(查询最近一次挂上去的中断对象,以及它当时是怎么挂上去的)VOIDKiGetVectorDispatch(INULONGVector,INPDISPATCH_INFODispatch){PKINTERRUPT_ROUTINEHandler;PVOIDCurrent;UCHARType;UCHAREntry;Entry=HalVectorToIDTEntry(Vector);//这个宏将中断向量号转换为IDT表项索引(一般相同)//固定为KiUnexpectedInterruptN函数的地址,N表示IRQDispatch->NoDispatch=(PVOID)(((ULONG_PTR)&KiStartUnexpectedRange)+(Entry-0x30)*KiUnexpectedEntrySize);Dispatch-〉InterruptDispatch=(PVOID)KiInterruptDispatch;//这个字段固定Dispatch->FloatingDispatch=NULL;//尚不支持Dispatch-〉ChainedDispatch=(PVOID)KiChainedDispatch;//这个字段固定Dispatch->FlatDispatch=NULL;Current=KeQueryInterruptHandler(Vector);//获得这个中断向量处当前存放的isr函数地址if((PKINTERRUPT_ROUTINE)Current==Dispatch->NoDispatch){Dispatch-〉Interrupt=NULL;//表示尚未挂接有任何中断对象Dispatch->Type=NoConnect;//表示尚未挂接有任何中断对象}else{//关键,可有isr(其实是个模板isr)反推出当前的中断对象(即最近一次挂上去的对象)Dispatch->Interrupt=CONTAINING_RECORD(Current,KINTERRUPT,DispatchCode);Handler=Dispatch->Interrupt->DispatchAddress;if(Handler==Dispatch->ChainedDispatch)Dispatch-〉Type=ChainConnect;//上次的中断对象是以链接方式挂上去的elseif((Handler==Dispatch->InterruptDispatch)||(Handler==Dispatch->FloatingDispatch)){Dispatch-〉Type=NormalConnect;//上次的中断对象是以普通方式挂上去的}elseDispatch-〉Type=UnknownConnect;//不确定上次的中断对象是怎么挂上去的}下面这个函数返回当前cpu上指定中断向量处的isr。注意:任一时刻,每个isr可能是个模板isr,可能是个用户自定义的isr,也可能没有isr(即以KiUnexpectedlnterruptN函数占位)。PVOIDKeQueryInterruptHandler(INULONGVector){PKIPCRPcr=(PKIPCR)KeGetPcr();UCHAREntry;Entry=HalVectorToIDTEntry(Vector);return(PVOID)(((Pcr->IDT[Entry].ExtendedOffset<<16)&0xFFFF0000)|(Pcr->IDT[Entry].Offset&0xFFFF));}真正的挂接操作是调用下面的函数完成的VOIDKiConnectVectorToInterrupt(INPKINTERRUPTInterrupt,INCONNECT_TYPEType)//挂接类型{DISPATCH_INFODispatch;PKINTERRUPT_ROUTINEHandler;//将要填到IDT表项中的最直接isrKiGetVectorDispatch(Interrupt->Vector,&Dispatch);if(Type==NoConnect)//if用户要撤销挂接Handler=Dispatch.NoDispatch;else{//填好本中断对象的dispatchisrInterrupt->DispatchAddress=(Type==NormalConnect)?Dispatch.InterruptDispatch:Dispatch.ChainedDispatch;Handler=(PVOID)&Interrupt-〉DispatchCode;//本中断对象的模板isr}//将本中断对象的模板isr或者KiUnexpectedlnterruptN填写到IDT的对应表项处。//可以看出,通过IoConnectinterrupt函数,IDT中的每个isr都是最后一次挂接的中断对象的模板isrKeRegisterInterruptHandler(Interrupt->Vector,Handler);}VOIDKeRegisterInterruptHandler(INULONGVector,INPVOIDHandler){UCHAREntry;ULONG_PTRAddress;PKIPCRPcr=(PKIPCR)KeGetPcr();Entry=HalVectorToIDTEntry(Vector);Address=PtrToUlong(Handler);//将isr填写到相应的表项中Pcr->IDT[Entry].ExtendedOffset=(USHORT)(Address>>16);Pcr->IDT[Entry].Offset=(USHORT)Address;}通过IoConnectInterrupt函数挂接的中断对象,都是将其模板isr填写到IDT表项中,这样,谁最后挂接,谁的模板isr就会最后覆写到那个表项处。如果使用了同一中断号的各个中断对象都是以链接方式挂接上去的,那么这些中断对象将组成一个链表。这样,当cpu收到对应的中断号时,会找到IDT中对应表项的isr给予执行。而那个isr就是最后挂接的中断对象的模板isr,这个模板isr的代码前面已看过,它将跳转进入下面的函数VOIDFASTCALLKiInterruptTemplateHandler(INPKTRAP_FRAMETrapFrame,//ecxINPKINTERRUPTInterrupt)//edx{KiEnterInterruptTrap(TrapFrame);((PKI_INTERRUPT_DISPATCH)Interrupt-〉DispatchAddress)(TrapFrame,Interrupt);//关键}看到没,每个中断对象的模板isr,会调用它的dispatchisr。以链接方式挂上去的中断对象的dispatchisr都是KiChainedDispatch,反之则是KilnterruptDispatch。我们看:VOIDFASTCALLKiChainedDispatch(INPKTRAP_FRAMETrapFrame,INPKINTERRUPTInterrupt){KIRQLOldIrql;BOOLEANHandled;PLIST_ENTRYNextEntry,ListHead;KeGetCurrentPrcb()-〉InterruptCount++;//递增中断计数//HalBeginSystemlnterrupt会提升irql至指定irql,以准备执行我们的isrif(HalBeginSystemInterrupt(Interrupt-〉Irql,Interrupt-〉Vector,&0ldIrql))//APIC相关{ListHead=&Interrupt->InterruptListEntry;NextEntry=ListHead;while(TRUE)//遍历中断对象链表,直至找到一个能处理这个中断的中断对象为止{if(Interrupt-〉SynchronizeIrql>Interrupt->Irql)//再次提升irql0ldIrql=KfRaiseIrql(Interrupt->SynchronizeIrql);KxAcquireSpinLock(Interrupt-〉ActualLock);//加锁,保障多cpu互斥//执行我们的isr(即用户自己提供的isr),注意返回值Handled=Interrupt->ServiceRoutine(Interrupt,Interrupt->ServiceContext);KxReleaseSpinLock(Interrupt->ActualLock);if(Interrupt->SynchronizeIrql>Interrupt->Irql)KfLowerIrql(0ldIrql);//if本中断对象认领了,且不许执行下一个中断对象就退出查找循环。//(LevelSensitive即FALSE,—般的中断对象都这样)if((Handled)&&(Interrupt->Mode==LevelSensitive))break;NextEntry=NextEntry->Flink;if(NextEntry==ListHead)//if链表中的最后一个中断对象{if(Interrupt->Mode==LevelSensitive)break;if(!Handled)break;//if没能处理这个中断,退出循环}Interrupt=CONTAINING_RECORD(NextEntry,KINTERRUPT,InterruptListEntry);}KiExitInterrupt(TrapFrame,OldIrql,FALSE);}Else//清理中断Trap帧,恢复中断现场,回到原断点处继续执行KiExitInterrupt(TrapFrame,OldIrql,TRUE);}用户自己的isr的原型是:BOOLEANInterruptService(__instruct_KINTERRUPT*Interrupt,__inPVOIDServiceContex);我们的这个isr应该根据ServiceContex判断这个中断是不是我们驱动中的设备发出的,若是,才能处理,返回TRUE。否则应返回FALSE,让系统继续寻找中断对象链表中的下一个中断对象去认领。而对于以普通覆写方式挂上去的中断对象,它的dispatchisr是KilnterruptDispatch,我们看:VOIDFASTCALLKiInterruptDispatch(INPKTRAP_FRAMETrapFrame,INPKINTERRUPTInterrupt){KIRQLOldIrql;KeGetCurrentPrcb()-〉InterruptCount++;//递增中断计数if(HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,Interrupt->Vector,&OldIrql)){KxAcquireSpinLock(Interrupt->ActualLock);//调用其自己的isrInterrupt->ServiceRoutine(Interrupt,Interrupt->ServiceContext);KxReleaseSpinLock(Interrupt->ActualLock);KiExitInterrupt(TrapFrame,OldIrql,FALSE);}elseKiExitInterrupt(TrapFrame,OldIrql,TRUE);}看到没,普通方式挂上去的中断对象,它独占中断号,当发生相应中断时,系统简单执行一下它自己的isr后,就返回了,不会有在链表中查找的过程。底层驱动在卸载时,往往要撤销挂接的那些中断,我们看下中断对象如如何撤销挂接的。VOIDIoDisconnectInterrupt(PKINTERRUPTInterruptObject){LONGi;PIO_INTERRUPTIoInterrupt;PAGED_CODE();IoInterrupt=CONTAINING_RECORD(InterruptObject,IO_INTERRUPT,FirstInterrupt);KeDisconnectInterrupt(&IoInterrupt->FirstInterrupt);//撤销第一个中断对象的挂接for(i=0;i<KeNumberProcessors;i++)〃撤销其它中断对象的挂接{if(IoInterrupt->Interrupt[i])KeDisconnectInterrupt(&InterruptObject[i]);}ExFreePool(IoInterrupt);//释放整个中断对象数组加头部占用的那块内存}BOOLEANKeDisconnectInterrupt(INPKINTERRUPTInterrupt){KIRQLOldIrql,Irql;ULONGVector;DISPATCH_INFODispatch;PKINTERRUPTNextInterrupt;BOOLEANState;KeSetSystemAffinityThread(1<<Interrupt->Number);OldIrql=KiAcquireDispatcherLock();State=Interrupt->Connected;if(State){Irql=Interrupt->Irql;Vector=Interrupt->Vector;KiGetVectorDispatch(Vector,&Dispatch);//获取上次的挂接信息if(Dispatch.Type==ChainConnect){ASSERT(Irql<=SYNCH_LEVEL);if(Interrupt==Dispatch.Interrupt)//if要撤销挂接的中断对象就是最近挂接的那个{Dispatch.Interrupt=CONTAINING_RECORD(Dispatch.Interrupt->InterruptListEntry.Flink,KINTERRUPT,InterruptListEntry);KiConnectVectorToInterrupt(Dispatch.Interrupt,ChainConnect);}//关键。脱出链表RemoveEntryList(&Interrupt->InterruptListEntry);NextInterrupt=CONTAINING_RECORD(Dispatch.Interrupt->InterruptListEntry.Flink,KINTERRUPT,InterruptListEntry);if(Dispatch.Interrupt==Nextinterrupt)//也即if链表中就剩下一个中断对象了KiConnectVectorToInterrupt(Dispatch.Interrupt,NormalConnect);//改为普通方式}}Else//if原来本身就只挂着一个中断对象{HalDisableSystemInterrupt(Interrupt->Vector,Irql);KiConnectVectorToInterrupt(Interrupt,NoConnect);//改为KiUnexpectedlnterruptN}Interrupt->Connected=FALSE;}KiReleaseDispatcherLock(OldIrql);KeRevertToUserAffinityThread();returnState;}通过IoConnectinterrupt函数挂接注册中断,确实为程序员减轻了大量负担。通过这种方式注册的isr,分三层。第一层是中断对象的模板isr,第二层是中断对象的dispatchisr,第三层才是用户自己提供的isr。每当发生中断时,系统逐层调用这三层isr。因此,也可以说,我们提供的那个isr被系统托管了,IDT表项中的isr是系统的托管isr。当然,程序员,也可以直接修改IDT中的表项,改成自己的isr,这就是所谓的isrhook(注意要进行isrhook的话,必须每个cpu都要hook)最后我们看一下典型的系统时钟中断是怎么处理的。系统每隔10ms产生一次时钟中断,时钟中断的IRQ固定是0,中断号默认映射到0x30,时钟中断的isr最终进入下面的函数。VOIDFASTCALLHalpClockInterruptHandler(INPKTRAP_FRAMETrapFrame){KIRQLIrql;KiEnterInterruptTrap(TrapFrame);//提升irql至CL0CK2_LEVELif(HalBeginSystemInterrupt(CL0CK2_LEVEL,0x30,&Irql))//0x30就是时钟中断的中断号{/*Updatetheperformancecounter*/HalpPerfCounter.QuadPart+=HalpCurrentRollOver;HalpPerfCounterCutoff=KiEnableTimerWatchdog;KeUpdateSystemTime(TrapFrame,HalpCurrentTimeIncrement,Irql);//关键函数}KiEoiHelper(TrapFrame);VOIDFASTCALLKeUpdateSystemTime(INPKTRAP_FRAMETrapFrame,INULONGIncrement,INKIRQLIrql){PKPRCBPrcb=KeGetCurrentPrcb();ULARGE_INTEGERCurrentTime,InterruptTime;ULONGHand,OldTickCount;//更新启动以来的运行时间计数InterruptTime.HighPart=SharedUserData->InterruptTime.High1Time;InterruptTime.LowPart=SharedUserData->InterruptTime.LowPart;InterruptTime.QuadPart+=Increment;//Increment—般为10msSharedUserData->InterruptTime.High1Time=InterruptTime.HighPart;SharedUserData->InterruptTime.LowPart=InterruptTime.LowPart;SharedUserData->InterruptTime.High

温馨提示

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

评论

0/150

提交评论