版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
中斷處理每個cpu有一張中斷表,簡稱IDT。IDT的整體布局:【異常->空白->5系->硬】(推薦采用7字口訣的方式重點記憶)異常:前20個表項存放著各個異常的描述符(IDT表不僅可以放中斷描述符,還放置了所有異常的異常處理描述符,0x00-0x13)保留:0x14-0x1F,忽略這塊號段空白:接下來存放一組空閑的保留項(0x20-0x29),供系統(tǒng)和程序員自己分配注冊使用5系:然后是系統(tǒng)自己注冊的5個預(yù)定義的軟中斷向量(軟中斷指手動的INT指令)(0x2A-0x2E5個系統(tǒng)預(yù)注冊的中斷向量,0x2A:KiGetTickCount,0x2B:KiCallbaclReturn0x2C:KiRaiseAssertion,0x2D:KiDebugService,0x2E:KiSystemService)硬:最后的表項供驅(qū)動程序注冊硬件中斷使用和自定義注冊其他軟中斷使用(0x30-0xFF)下面是中斷號的具體的分配情況:0x00-0x13固定分配給異常:0x14-0x1f:Intel保留給他公司將來自己使用(OS和用戶都不要試圖去使用這個號段,不安全) 以下的號段可用于自由分配給OS、硬件、用戶使用 linux等其他系統(tǒng)是怎么劃分這塊號段的,不管,我們只看Windows的情況0x20-0x29:Windows沒占用,因此這塊號段我們也可以自由使用0x2A-0x2E:Windows自己本身使用的5個中斷號0x30-0xFF:Windows決定把這塊剩余的號段讓給硬件和用戶使用參見《寒江獨釣》一書P93頁注冊鍵盤中斷時,搜索空閑未用表項是從0x20開始,到0x29結(jié)束的,就知道為什么寒江獨釣是在這段范圍內(nèi)搜索空白表項了(其實我們也完全可以從0x14開始搜索)Windows系統(tǒng)中,0x30-0xFF這塊號段讓給了硬件和用戶自己使用。事實上,這塊號段的開頭部分默認(rèn)都是讓給硬件IRQ使用的,也即是分配給硬件IRQ的。IRQN默認(rèn)映射到中斷號0x30+N,如IRQ0用于系統(tǒng)時鐘,系統(tǒng)時鐘中斷號默認(rèn)對應(yīng)就是0x30。當(dāng)然程序員也可以修改APIC(可編程中斷控制器)將IRQ映射到自定義的中斷號。IRQ對外部設(shè)備分配,但IRQ0,IRQ2,IRQ13必須如下分配:IRQ0 >間隔定時設(shè)備IRQ2 >8259A芯片IRQ13 >外部數(shù)學(xué)協(xié)處理器其余的IRQ可以任意分配給外部設(shè)備。雖然一個IRQ只對應(yīng)一個中斷號,但是由于IRQ數(shù)量有限,而設(shè)備種類成千上萬,因此多個設(shè)備可以使用同一個IRQ,進而,多個設(shè)備可以分配同一個中斷號。因此,一個中斷號可以共享給多個設(shè)備同時使用。Pnp設(shè)備在插入系統(tǒng)后,相應(yīng)的總線驅(qū)動會自動為其創(chuàng)建一個用作棧底基石的pdo,然后給這個pdo發(fā)出一個IRP_MN_QUERY_RESOURCE_REQUIREMENTS,查詢得到初步的資源需求。然后,pnp管理器會找到相應(yīng)的硬件端口驅(qū)動,調(diào)用其AddDevice函數(shù),當(dāng)這個函數(shù)返回后,該硬件設(shè)備的設(shè)備棧已經(jīng)建立起立了,pnp管理器就給棧頂設(shè)備發(fā)出一個IRP_MN_FILTER_RESOURCE_REQUIREMENTS再次詢問該硬件需要的資源(功能驅(qū)動此時可以攔截處理這個irp,修改資源需求),當(dāng)確定好最終的資源需求后,系統(tǒng)就協(xié)調(diào)分配端口號、中斷號、DIRQL等硬件資源給它。分配完后,就發(fā)出一個IRP_MN_START_DEVICE給棧頂設(shè)備請求啟動該硬件設(shè)備。當(dāng)該irp下發(fā)來到端口驅(qū)動(指真正的硬件驅(qū)動)時,端口驅(qū)動這時就需要在分配的中斷號上注冊一個中斷服務(wù)例程,以處理硬件中斷,與設(shè)備進行交互。下面的函數(shù)就是用來注冊中斷服務(wù)例程的(準(zhǔn)確的說法叫‘掛接中斷')NTSTATUSIoConnectInterrupt(OUTPKINTERRUPT*InterruptObject,//返回創(chuàng)建的中斷對象(一般是一個數(shù)組)INPKSERVICE_ROUTINEServiceRoutine,//我們的isr(ourisr)INPVOIDServiceContext,//isr的參數(shù)INPKSPIN_LOCKSpinLock,//我們isr的自旋鎖,用于多cpu互斥(一般傳NULL即可)INULONGVector,//分配到的中斷號INKIRQLIrql,//isr對應(yīng)的irqlINKIRQLSynchronizeIrql,//必須〉=Irql,—般=Irql即可(isr實際運行在這個irql)INKINTERRUPT_MODEInterruptMode,//表示是否允許執(zhí)行本中斷的下一個中斷INBOOLEANShareVector,//表示本中斷對象是否想要共享中斷號以及是否允許共享INKAFFINITYProcessorEnableMask,//本isr的cpu親緣性,一般全部cpu都親緣。INBOOLEANFloatingSave)//—般為FALSE{PKINTERRUPTInterrupt;PKINTERRUPTInterruptUsed;//當(dāng)前的中斷對象PIO_INTERRUPTIoInterrupt;//中斷對象數(shù)組的頭部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;}//上面的循環(huán)根據(jù)本isr可以在哪些cpu上運行,得出可運行的cpu個數(shù)if(!Count)returnSTATUS_INVALID_PARAMETER;//分配一個中斷對象數(shù)組IoInterrupt=ExAllocatePoolWithTag(NonPagedPool,(Count-1)*sizeof(KINTERRUPT)+sizeof(IO_INTERRUPT),TAG_KINTERRUPT);if(!IoInterrupt)returnSTATUS_INSUFFICIENT_RESOURCES;*InterruptObject=&IoInterrupt->FirstInterrupt;//if用戶沒提供自旋鎖,就使用內(nèi)置的自旋鎖。一般用戶不需自己提供自旋鎖SpinLockUsed=SpinLock?SpinLock:&IoInterrupt->SpinLock;Interrupt=(PKINTERRUPT)(IoInterrupt+1);//后面的中斷對象數(shù)組地址FirstRun=TRUE;RtlZeroMemory(IoInterrupt,sizeof(IO_INTERRUPT));Affinity=ProcessorEnableMask&KeActiveProcessors;for(Count=0;Affinity;Count++,Affinity>>=1)//Count其實表示cpu號{if(Affinity&1){//第一次使用頭部中的那個內(nèi)置中斷對象InterruptUsed=FirstRun?&IoInterrupt->FirstInterrupt:Interrupt;//構(gòu)造一個中斷對象KeInitializeInterrupt(InterruptUsed,ServiceRoutine,ServiceContext,SpinLockUsed,Vector,Irql,SynchronizeIrql,InterruptMode,ShareVector,Count,FloatingSave);if(!KeConnectInterrupt(InterruptUsed))//關(guān)鍵,掛接中斷對象到目標(biāo)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;}如上,這個函數(shù)用來將指定isr掛接到各個cpu的指定中斷號上。因為在多cpu系統(tǒng)中,一個設(shè)備可以向每個cpu都發(fā)出中斷,因此,必須在每個cpu的IDT中都要掛接登記那個中斷的isr。具體是怎么掛接的呢?這個函數(shù)會創(chuàng)建一個中斷對象數(shù)組,然后將各個中斷對象對應(yīng)掛接到各cpu的同一中斷號上。由于老式機器是單cpu的,因此,早期的中斷對象結(jié)構(gòu)IO_INTERRUPT就包含一個中斷對象任意,后來的機器對其進行了擴展,在這個結(jié)構(gòu)后面是一個中斷對象數(shù)組,用來掛接到其他cpu上。另外,由于多個設(shè)備可以共用同一中斷號,所以每個中斷號需要一個自己的鏈表來記錄所有掛接在此中斷號上的所有中斷對象。typedefstruct_IO_INTERRUPT{KINTERRUPTFirstlnterrupt;//內(nèi)置的中斷對象PKINTERRUPTInterrupt[MAXIMUM_PROCESSORS];//記錄各cpu上掛接的中斷對象地址KSPIN_LOCKSpinLock;//內(nèi)置的isr自旋鎖,如果用戶沒提供,就默認(rèn)使用這個公共的自旋鎖。}IO_INTERRUPT,*PIO_INTERRUPT;該結(jié)構(gòu)體后面緊跟一個INTERRUPT結(jié)構(gòu)體數(shù)組typedefstruct_KINTERRUPT//中斷對象{CSHORTType;CSHORTSize;LIST_ENTRYInterruptListEntry;//用來掛入中斷對象鏈表PKSERVICE_ROUTINEServiceRoutine;//我們的isr(用戶的isr)PVOIDServiceContext;//isr參數(shù)KSPIN_LOCKSpinLock;//一般無用ULONGTickCount;//沒用PKSPIN_LOCKActualLock;//我們isr實際使用的自旋鎖PKINTERRUPT_ROUTINEDispatchAddress;//中間的Dispatchisr函數(shù)地址CCHARNumber;//要掛往的目標(biāo)cpuULONGVector;//要掛往的目標(biāo)中斷號KIRQLIrql;//isr對應(yīng)的isrKIRQLSynchronizelrql;//isr實際運行在的irqlBOOLEANFloatingSave;//—般為FALSEBOOLEANConnected;//表示本中斷對象是否掛上去了BOOLEANShareVector;//是否想要共享中斷號,以及是否允許后來的中斷對象共享KINTERRUPT_MODEMode;//是否允許繼續(xù)執(zhí)行本中斷對象后面的中斷對象的isrULONGServiceCount;//沒用ULONGDispatchCount;//沒用ULONGDispatchCode[DISPATCH_LENGTH];//這不是數(shù)組,而是一段代碼,表示本中斷對象的模板isr}KINTERRUPT;下面的函數(shù)用來構(gòu)造、初始化一個中斷對象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)//由于這個函數(shù)未導(dǎo)出,由系統(tǒng)內(nèi)部調(diào)用,傳的SpinLock參數(shù)很少為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的字節(jié)碼到中斷對象內(nèi)部for(i=0;i<DISPATCH_LENGTH;i++)*DispatchCode++=((PULONG)KiInterruptTemplate)[i];//patch指向模板isr中的movedx,0指令的操作數(shù)部分Patch=(PULONG)((ULONG)Patch+((ULONG)&KiInterruptTemplateObject-4-(ULONG)KiInterruptTemplate));*Patch=PtrToUlong(Interrupt);//也即將原movedx,0改為movedx,本中斷對象的地址Interrupt-〉Connected=FALSE;//尚未掛入}下面是系統(tǒng)的模板isr:_KiInterruptTemplate:KiEnterTrapKI_PUSH_FAKE_ERROR_CODE_KiInterruptTemplate2ndDispatch:movedx,0//這條指令的操作數(shù)0將被動態(tài)修改成具體中斷對象的地址_KiInterruptTemplateObject:moveax,offset@KiInterruptTemplateHandler@8//KilnterruptTemplateHandler函數(shù)jmpeax_KiInterruptTemplateDispatch:上面就是系統(tǒng)的模板isr,每個中斷對象的模板isr就是從系統(tǒng)的模板isr復(fù)制過來的,然后稍作修改。當(dāng)構(gòu)造好中斷對象后,就需要把它掛接到目標(biāo)cpu的目標(biāo)中斷號上。下面的函數(shù)就這個用途BOOLEAN//返回值表示是否掛接成功KeConnectInterrupt(INPKINTERRUPTInterrupt){BOOLEANConnected,Error,Status;KIRQLIrql,OldIrql;UCHARNumber;ULONGVector;DISPATCH_INFODispatch;Number=Interrupt-〉Number;//目標(biāo)cpuVector=Interrupt-〉Vector;//目標(biāo)中斷號Irql=Interrupt->Irql;//SynchronizeIrql必須>=Irqlif((Irql>HIGH_LEVEL)||(Number>=KeNumberProcessors)||(Interrupt->SynchronizeIrql<Irql)||(Interrupt->FloatingSave))returnFALSE;}Connected=FALSE;Error=FALSE;KeSetSystemAffinityThread(l<<Number);//改變當(dāng)前線程的cpu親緣性先,挪到目標(biāo)cpu上去運行 華麗的分割線 //下面的這些代碼已經(jīng)處在目標(biāo)cpu上運行了OldIrql=KiAcquireDispatcherLock();if(!Interrupt-〉Connected)//if尚未掛接{//查詢當(dāng)前cpu這個中斷號上的最近一次(也即上一次)的掛接情況KiGetVectorDispatch(Vector,&Dispatch);if(Dispatch.Type==NoConnect)//如果這個中斷號尚未掛接有任何中斷對象{Interrupt->Connected=Connected=TRUE;InitializeListHead(&Interrupt-〉InterruptListEntry);//獨立//NormalConnect表示以普通方式掛上去(非鏈接方式),相當(dāng)于覆蓋方式KiConnectVectorToInterrupt(Interrupt,NormalConnect);Status=HalEnableSystemInterrupt(Vector,Irql,Interrupt-〉Mode);//APIC相關(guān)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);}//關(guān)鍵。掛在上一個中斷對象的后面InsertTailList(&Dispatch.Interrupt->InterruptListEntry,&Interrupt->InterruptListEntry);}}KiReleaseDispatcherLock(OldIrql);KeRevertToUserAffinityThread();if((Connected)&&(Error)){KeDisconnectInterrupt(Interrupt);Connected=FALSE;}returnConnected;}下面的函數(shù)用于查詢當(dāng)前cpu指定中斷號上的最近一次掛接情況(查詢最近一次掛上去的中斷對象,以及它當(dāng)時是怎么掛上去的)VOIDKiGetVectorDispatch(INULONGVector,INPDISPATCH_INFODispatch){PKINTERRUPT_ROUTINEHandler;PVOIDCurrent;UCHARType;UCHAREntry;Entry=HalVectorToIDTEntry(Vector);//這個宏將中斷向量號轉(zhuǎn)換為IDT表項索引(一般相同)//固定為KiUnexpectedInterruptN函數(shù)的地址,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);//獲得這個中斷向量處當(dāng)前存放的isr函數(shù)地址if((PKINTERRUPT_ROUTINE)Current==Dispatch->NoDispatch){Dispatch-〉Interrupt=NULL;//表示尚未掛接有任何中斷對象Dispatch->Type=NoConnect;//表示尚未掛接有任何中斷對象}else{//關(guān)鍵,可有isr(其實是個模板isr)反推出當(dāng)前的中斷對象(即最近一次掛上去的對象)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;//不確定上次的中斷對象是怎么掛上去的}下面這個函數(shù)返回當(dāng)前cpu上指定中斷向量處的isr。注意:任一時刻,每個isr可能是個模板isr,可能是個用戶自定義的isr,也可能沒有isr(即以KiUnexpectedlnterruptN函數(shù)占位)。PVOIDKeQueryInterruptHandler(INULONGVector){PKIPCRPcr=(PKIPCR)KeGetPcr();UCHAREntry;Entry=HalVectorToIDTEntry(Vector);return(PVOID)(((Pcr->IDT[Entry].ExtendedOffset<<16)&0xFFFF0000)|(Pcr->IDT[Entry].Offset&0xFFFF));}真正的掛接操作是調(diào)用下面的函數(shù)完成的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的對應(yīng)表項處。//可以看出,通過IoConnectinterrupt函數(shù),IDT中的每個isr都是最后一次掛接的中斷對象的模板isrKeRegisterInterruptHandler(Interrupt->Vector,Handler);}VOIDKeRegisterInterruptHandler(INULONGVector,INPVOIDHandler){UCHAREntry;ULONG_PTRAddress;PKIPCRPcr=(PKIPCR)KeGetPcr();Entry=HalVectorToIDTEntry(Vector);Address=PtrToUlong(Handler);//將isr填寫到相應(yīng)的表項中Pcr->IDT[Entry].ExtendedOffset=(USHORT)(Address>>16);Pcr->IDT[Entry].Offset=(USHORT)Address;}通過IoConnectInterrupt函數(shù)掛接的中斷對象,都是將其模板isr填寫到IDT表項中,這樣,誰最后掛接,誰的模板isr就會最后覆寫到那個表項處。如果使用了同一中斷號的各個中斷對象都是以鏈接方式掛接上去的,那么這些中斷對象將組成一個鏈表。這樣,當(dāng)cpu收到對應(yīng)的中斷號時,會找到IDT中對應(yīng)表項的isr給予執(zhí)行。而那個isr就是最后掛接的中斷對象的模板isr,這個模板isr的代碼前面已看過,它將跳轉(zhuǎn)進入下面的函數(shù)VOIDFASTCALLKiInterruptTemplateHandler(INPKTRAP_FRAMETrapFrame,//ecxINPKINTERRUPTInterrupt)//edx{KiEnterInterruptTrap(TrapFrame);((PKI_INTERRUPT_DISPATCH)Interrupt-〉DispatchAddress)(TrapFrame,Interrupt);//關(guān)鍵}看到?jīng)],每個中斷對象的模板isr,會調(diào)用它的dispatchisr。以鏈接方式掛上去的中斷對象的dispatchisr都是KiChainedDispatch,反之則是KilnterruptDispatch。我們看:VOIDFASTCALLKiChainedDispatch(INPKTRAP_FRAMETrapFrame,INPKINTERRUPTInterrupt){KIRQLOldIrql;BOOLEANHandled;PLIST_ENTRYNextEntry,ListHead;KeGetCurrentPrcb()-〉InterruptCount++;//遞增中斷計數(shù)//HalBeginSystemlnterrupt會提升irql至指定irql,以準(zhǔn)備執(zhí)行我們的isrif(HalBeginSystemInterrupt(Interrupt-〉Irql,Interrupt-〉Vector,&0ldIrql))//APIC相關(guān){ListHead=&Interrupt->InterruptListEntry;NextEntry=ListHead;while(TRUE)//遍歷中斷對象鏈表,直至找到一個能處理這個中斷的中斷對象為止{if(Interrupt-〉SynchronizeIrql>Interrupt->Irql)//再次提升irql0ldIrql=KfRaiseIrql(Interrupt->SynchronizeIrql);KxAcquireSpinLock(Interrupt-〉A(chǔ)ctualLock);//加鎖,保障多cpu互斥//執(zhí)行我們的isr(即用戶自己提供的isr),注意返回值Handled=Interrupt->ServiceRoutine(Interrupt,Interrupt->ServiceContext);KxReleaseSpinLock(Interrupt->ActualLock);if(Interrupt->SynchronizeIrql>Interrupt->Irql)KfLowerIrql(0ldIrql);//if本中斷對象認(rèn)領(lǐng)了,且不許執(zhí)行下一個中斷對象就退出查找循環(huán)。//(LevelSensitive即FALSE,—般的中斷對象都這樣)if((Handled)&&(Interrupt->Mode==LevelSensitive))break;NextEntry=NextEntry->Flink;if(NextEntry==ListHead)//if鏈表中的最后一個中斷對象{if(Interrupt->Mode==LevelSensitive)break;if(!Handled)break;//if沒能處理這個中斷,退出循環(huán)}Interrupt=CONTAINING_RECORD(NextEntry,KINTERRUPT,InterruptListEntry);}KiExitInterrupt(TrapFrame,OldIrql,FALSE);}Else//清理中斷Trap幀,恢復(fù)中斷現(xiàn)場,回到原斷點處繼續(xù)執(zhí)行KiExitInterrupt(TrapFrame,OldIrql,TRUE);}用戶自己的isr的原型是:BOOLEANInterruptService(__instruct_KINTERRUPT*Interrupt,__inPVOIDServiceContex);我們的這個isr應(yīng)該根據(jù)ServiceContex判斷這個中斷是不是我們驅(qū)動中的設(shè)備發(fā)出的,若是,才能處理,返回TRUE。否則應(yīng)返回FALSE,讓系統(tǒng)繼續(xù)尋找中斷對象鏈表中的下一個中斷對象去認(rèn)領(lǐng)。而對于以普通覆寫方式掛上去的中斷對象,它的dispatchisr是KilnterruptDispatch,我們看:VOIDFASTCALLKiInterruptDispatch(INPKTRAP_FRAMETrapFrame,INPKINTERRUPTInterrupt){KIRQLOldIrql;KeGetCurrentPrcb()-〉InterruptCount++;//遞增中斷計數(shù)if(HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,Interrupt->Vector,&OldIrql)){KxAcquireSpinLock(Interrupt->ActualLock);//調(diào)用其自己的isrInterrupt->ServiceRoutine(Interrupt,Interrupt->ServiceContext);KxReleaseSpinLock(Interrupt->ActualLock);KiExitInterrupt(TrapFrame,OldIrql,FALSE);}elseKiExitInterrupt(TrapFrame,OldIrql,TRUE);}看到?jīng)],普通方式掛上去的中斷對象,它獨占中斷號,當(dāng)發(fā)生相應(yīng)中斷時,系統(tǒng)簡單執(zhí)行一下它自己的isr后,就返回了,不會有在鏈表中查找的過程。底層驅(qū)動在卸載時,往往要撤銷掛接的那些中斷,我們看下中斷對象如如何撤銷掛接的。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);//釋放整個中斷對象數(shù)組加頭部占用的那塊內(nèi)存}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);}//關(guān)鍵。脫出鏈表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函數(shù)掛接注冊中斷,確實為程序員減輕了大量負(fù)擔(dān)。通過這種方式注冊的isr,分三層。第一層是中斷對象的模板isr,第二層是中斷對象的dispatchisr,第三層才是用戶自己提供的isr。每當(dāng)發(fā)生中斷時,系統(tǒng)逐層調(diào)用這三層isr。因此,也可以說,我們提供的那個isr被系統(tǒng)托管了,IDT表項中的isr是系統(tǒng)的托管isr。當(dāng)然,程序員,也可以直接修改IDT中的表項,改成自己的isr,這就是所謂的isrhook(注意要進行isrhook的話,必須每個cpu都要hook)最后我們看一下典型的系統(tǒng)時鐘中斷是怎么處理的。系統(tǒng)每隔10ms產(chǎn)生一次時鐘中斷,時鐘中斷的IRQ固定是0,中斷號默認(rèn)映射到0x30,時鐘中斷的isr最終進入下面的函數(shù)。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);//關(guān)鍵函數(shù)}KiEoiHelper(TrapFrame);VOIDFASTCALLKeUpdateSystemTime(INPKTRAP_FRAMETrapFrame,INULONGIncrement,INKIRQLIrql){PKPRCBPrcb=KeGetCurrentPrcb();ULARGE_INTEGERCurrentTime,InterruptTime;ULONGHand,OldTickCount;//更新啟動以來的運行時間計數(shù)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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二手機器轉(zhuǎn)讓簡單合同范本年
- 施工工程運輸合同范本
- 購買二手房買賣合同范本
- 2025cc直播平臺主播轉(zhuǎn)公會合同
- 二手商品房買賣合同
- 水泥銷售合同范本
- 石料買賣合同
- 2025續(xù)訂勞動合同通知書模板
- 2025建筑企業(yè)流動資金借款合同范本版
- 廣告發(fā)布投放合同
- 少兒口才培訓(xùn)主持課件
- 新《學(xué)前教育法》知識講座課件
- 公文寫作題庫(500道)
- 學(xué)校教學(xué)常規(guī)管理學(xué)習(xí)活動課件
- 餐飲業(yè)績效考核表(店長、前廳領(lǐng)班、吧臺、廚師長、后廚、服務(wù)員、收銀員、庫管、后勤)3
- 集成墻板購銷合同范本(2024版)
- 骨髓穿刺課件
- 2024中國保險發(fā)展報告-中南大風(fēng)險管理研究中心.燕道數(shù)科
- 元素的用途完整版本
- 建筑設(shè)計工程設(shè)計方案
- 供熱行業(yè)環(huán)境保護管理辦法
評論
0/150
提交評論