Trap Dispatching,陷阱分发:
中断(interrupt)和异常(exception):操作系统遇到特定条件时跳出正常的流控制。
Trap, 陷阱,处理器在异常或中断发生时捕捉正在执行的线程,并且把控制权交给系统中特定位置的机制。Windows中由陷阱处理器(trap handler)来处理特定的异常或中断。包括处理中断的中断服务例程,处理系统服务调用的系统服务,处理硬件异常和软件异常的异常分发器(exception dispatcher),以及处理虚拟地址异常的虚拟内存分页管理器。
中断通常是指可以在任何时候发生的异步事件,与处理器正在执行的任务没有必然联系。主要由I/O设备、处理器时钟、定时器等产生。
异常通常是指同步事件,是特定指令的执行结果。主要包括内存访问异常,调试器指令,除零等。
当硬件异常或中断产生时,处理器会在中断线程的内核栈上记录足够的状态,以便在没有发生中断任务时返回继续执行。如果线程执行在用户模式,OS会切换到该线程的内核模式栈上,在中断线程的内核模式栈上创建一个陷阱帧(trap frame)以保存该线程的执行状态,陷阱帧是线程完整上下文的子集,可以通过dt nt!_ktrap_frame查看其定义。
2: kd> dt nt!_ktrap_frame
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
在大多数情况下,内核会在将控制权交给相应的陷阱处理器之前设置相应的前端陷阱处理函数,进行一般的陷阱处理任务。对于不应发生或无法处理的异常,对应的函数为KeBugCheckEx。
I/O设备是最典型的硬件中断产生设备。在需要服务时通过中断通知处理器,完成任务时再次通知处理器。这样处理器可以在相应的时间内处理其他任务。系统软件也可以产生中断,如线程分发等。内核可以禁止中断,但通常只在处理中断分发异常时如此。内核通过安装中断陷阱处理器对设备中断进行响应,中断发生时可以将控制权交给中断服务例程(ISR)(设备驱动提供支持)或内核例程进行处理。
硬件中断处理过程:
中断控制器连接多个外部I/O设备,当中断发生时,中断控制器通知处理器其中一个中断发生,处理器从中断控制器获得中断请求(IRQ: interrupt request)。中断控制器将IRQ转化为可由中断分发表(IDT: interrupt dispatching table)索引的中断号,中断号可以在IDT中索引到合适的中断分发例程。IDT在系统启动时填充了指向各个中断/异常处理例程的指针。
IDT可由!idt查看。多个设备可以共享一个中断号。
windows将硬件IRQ映射为IDT中断号,系统通过IDT配置各种异常的处理例程。Windows最多支持256个IDT入口,系统实际支持的IRQ数由使用的中断控制器决定。
2: kd> !idt
Dumping IDT:
37: 82c31104 hal!PicSpuriousService37
51: 85ac4058 i8042prt+0x2CF9 (KINTERRUPT 85ac4000)
52: 85570058 USBPORT+0x1FDA (KINTERRUPT 85570000)
61: 85ac42d8 i8042prt+0x749A (KINTERRUPT 85ac4280)
62: 85892cd8 dxgkrnl!DpiFdoMessageInterruptRoutine (KINTERRUPT 85892c80)
70: 84870058 pci!ExpressRootPortMessageRoutine (KINTERRUPT 84870000)
72: 85570558 HDAudBus+0x2F00 (KINTERRUPT 85570500)
HDAudBus+0x2F00 (KINTERRUPT 85ac4c80)
80: 848702d8 pci!ExpressRootPortMessageRoutine (KINTERRUPT 84870280)
82: 85ac4a58 ndis!NdisMSynchronizeWithInterrupt+0x6E3 (KINTERRUPT 85ac4a00)
90: 84870558 pci!ExpressRootPortMessageRoutine (KINTERRUPT 84870500)
92: 85ac47d8 ndis!NdisIMNotifyPnPEvent+0x4C85 (KINTERRUPT 85ac4780)
a0: 848707d8 pci!ExpressRootPortMessageRoutine (KINTERRUPT 84870780)
a2: 85ac4558 USBPORT+0x1FDA (KINTERRUPT 85ac4500)
b0: 84870a58 pci!ExpressRootPortMessageRoutine (KINTERRUPT 84870a00)
b1: 84870cd8 ACPI+0xE2D2 (KINTERRUPT 84870c80)
b2: 855702d8 ataport!AtaPortInitialize+0x3FA0 (KINTERRUPT 85570280)
ataport!AtaPortInitialize+0x3FA0 (KINTERRUPT 85570c80)
ataport!AtaPortInitialize+0x3FA0 (KINTERRUPT 85570a00)
ataport!AtaPortInitialize+0x3FA0 (KINTERRUPT 85570780)
c1: 82c313f4 hal!HalpBroadcastCallService
d1: 82c1a2d8 hal!HalpClockInterruptPn
d2: 82c19898 hal!HalpHpetRolloverInterrupt
df: 82c311dc hal!HalpApicRebootService
e1: 82c31958 hal!HalpIpiHandler
e3: 82c316f8 hal!HalpLocalApicErrorService
fd: 82c31f2c hal!HalpProfileInterrupt
fe: 82c321a8 hal!HalpPerfInterrupt
x86中断控制器:
PIC: Programmable Interrupt Controller
单处理器系统,15条中断线
APIC: Advanced Programmable Interrupt Controller
多处理器系统,256条中断线,兼容PIC
i8259A PIC, i82459 APIC
APIC由几个组件组成:
I/O APIC接受I/O设备中断,实现中断选择算法(HAL层中,软件实现),维持各处理器中断的负载平衡,并且提高效率(将中断分发到刚处理过同类中断的那个处理器上)
Local APIC接收I/O APIC从私有APIC总线上发送来的数据并中断相应的处理器
i8259A负责将APIC输入转化为相应的PIC输入
x64中断控制器:
兼容x86中断控制器,不能在不支持APIC的系统上运行
IA64中断控制器:
SAPIC: Streamlined Advanced Programmable Interrupt Controller
SAPIC与APIC区别:
APIC通过私有APIC总线传递中断,SAPIC通过I/O和系统总线加快传递速度。
APIC由APIC总线进行中断路由选择和负载平衡,SAPIC则由firmware编程进行,但windwos忽略firmware中的算法而采用round-robin算法。
相关命令:
!pic, APIC HAL上不work
!apic, 注意切换到响应的处理器上查看当前apic
!iopic, 显示I/O APIC配置
Software Interrupt Request Levels: IRQL
中断控制器实现了中断优先级,但Windows强迫使用自己的中断优先级IRQL。
x86: 0-31;x64&IA64: 0-15. 数值越大,优先级越高。内核为软件定义了标准IRQL。
中断按照优先级来处理,高优先级的中断会抢占低优先级的中断,低级别的中断会被屏蔽。中断发生时,处理器保存被中断线程的状态,并调用与该中断相关的陷阱分发器,提升处理器IRQL,然后调用中断服务例程。中断服务例程执行完毕后,降低处理器IRQL,恢复被保存的机器状态。内核模式线程可以通过KeRaiseIrql/KeLowerIrql直接提升/降低处理器的IRQL,或者通过获取内核同步对象的函数间接提升/降低处理器的IRQL。
延迟IRQL: PIC访问较慢,因此在IRQL提升后,由HAL记录新的IRQL。如果有低优先级的中断发生,则HAL改变中断屏蔽值的设置,并且延迟该低优先级中断直到IRQL降下来为止;如果没有低优先级的中断发生,则无需修改PIC的中断屏蔽值。
系统所有组件、内核、设备驱动,都试图保持IRQL在Passive级别,以便没有更高优先级的中断时可以及时响应硬件中断。
提升IRQL会阻塞同级别或低级别中断。例外:APC_LEVEL。若一个线程提升IRQL至APC_LEVEL,又由于DPC/Dispatch中断被重新调度,则系统会把APC_LEVEL中断递交给新调度的线程。
PCR(processor control region)和PRCB(processor control block)数据结构中包含IRQL域。
相关命令:
!irql,查看IRQL
!pcr, !prcb
2: kd> !pcr
KPCR for Processor 2 at 8a500000:
Major 1 Minor 1
NtTib.ExceptionList: 91b7208c
NtTib.StackBase: 00000000
NtTib.StackLimit: 00000000
NtTib.SubSystemTib: 8a503750
NtTib.Version: 0000fdd1
NtTib.UserPointer: 00000004
NtTib.SelfTib: 00000000
SelfPcr: 8a500000
Prcb: 8a500120
Irql: 0000001f
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 8a509020
GDT: 8a508c20
TSS: 8a503750
CurrentThread: 857f66d0
NextThread: 00000000
IdleThread: 8a505800
DpcQueue: 0x8a503520 0x828fa257 [Normal] nt!PpmPerfAction
2: kd> !prcb
PRCB for Processor 2 at 8a500120:
Current IRQL -- 28
Threads-- Current 857f66d0 Next 00000000 Idle 8a505800
Processor Index 2 Number (0, 2) GroupSetMember 00000004
Interrupt Count -- 00001ebc
Times -- Dpc 00000038 Interrupt 00000062
Kernel 00001051 User 0000007e
只有内核模式可以更改IRQL。在执行用户模式代码时,IRQL总在passive上,只有执行内核模式代码,IRQL才可能更高。
IRQL由HAL将IRQ按照一定方式映射得来。总线型驱动可以确定总线上的设备,确定一个设备可以分配哪些中断,然后将信息报给即插即用管理器,由即插即用管理器考虑其他设备后,确定为每个设备分配中断,然后调用HAL函数HalpGetSystemInterruptVector将中断映射到对应的IRQL。不同HAL版本的分配方式不同。
几个IRQL:
HIGH: KeBugCheckEx
Inter-processor interrupt: 向另一个处理器请求执行一个动作
Clock: 系统时钟,跟踪具体时刻,测量线程,分配CPU时间
Profile: 内核性能剖析功能,Kernrate工具
设备IRQL:映射了不同设备IRQ
DPC, APC: 内核和设备驱动产生的软件中断
Passive: 普通线程运行时的级别,允许所有中断
HIGH: KeBugCheckEx
Inter-processor interrupt: 向另一个处理器请求执行一个动作
Clock: 系统时钟,跟踪具体时刻,测量线程,分配CPU时间
Profile: 内核性能剖析功能,Kernrate工具
设备IRQL:映射了不同设备IRQ
DPC, APC: 内核和设备驱动产生的软件中断
Passive: 普通线程运行时的级别,允许所有中断
DPC/Dispatch及以上级别不能等待对象,只能访问non-paged内存。若违反,则会触发IRQL_NOT_LESS_OR_EUQAL错误。Driver verifier可以检查。
Interrupt object中断对象:内核控制对象,允许设备驱动为设备注册ISR,包含ISR地址、中断时的IRQL、与该ISR关联的IDT。初始化中断对象时,分发代码从KiInterruptTemplate中拷贝得到。中断发生时,这些代码被执行,调用实际的中断分发器,KiInterruptDispatch(适用于仅注册了一个中断对象的中断向量)或KiChainedDispatch(用于在多个中断对象之间共享的中断向量)例程。
2: kd> dt nt!_kinterrupt
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 InterruptListEntry : _LIST_ENTRY
+0x00c ServiceRoutine : Ptr32 unsigned char
+0x010 MessageServiceRoutine : Ptr32 unsigned char
+0x014 MessageIndex : Uint4B
+0x018 ServiceContext : Ptr32 Void
+0x01c SpinLock : Uint4B
+0x020 TickCount : Uint4B
+0x024 ActualLock : Ptr32 Uint4B
+0x028 DispatchAddress : Ptr32 void
+0x02c Vector : Uint4B
+0x030 Irql : UChar
+0x031 SynchronizeIrql : UChar
+0x032 FloatingSave : UChar
+0x033 Connected : UChar
+0x034 Number : Uint4B
+0x038 ShareVector : UChar
+0x039 Pad : [3] Char
+0x03c Mode : _KINTERRUPT_MODE
+0x040 Polarity : _KINTERRUPT_POLARITY
+0x044 ServiceCount : Uint4B
+0x048 DispatchCount : Uint4B
+0x050 Rsvd1 : Uint8B
+0x058 DispatchCode : [135] Uint4B
中断控制流:
设备->CPU中断控制器->IDT->中断对象->KiInterruptDispatch->驱动程序ISR
设备->CPU中断控制器->IDT->中断对象->KiInterruptDispatch->驱动程序ISR
Windows不是一个实时操作系统,VenturCom等第三方厂商提供了实时内核。
IoConnectInterrupt,connecting an interrupt object,将ISR与某个特定的中断级别关联起来,通常在设备驱动加载到系统中进行。
IoDisconnectInterrupt,disconnecting an interrupt object,将ISR与IDT项断开关联,通常在设备驱动卸载时进行。
中断对象的优势:避免设备驱动直接操纵中断硬件,无需知道IDT细节,易于同步ISR执行过程中可能共享数据的部分,易于为任何一个中断级别调用多个ISR(daisy-chain结构,几个设备共享同一中断线)。
Deferred Procedure Call(DPC),延迟过程调用。完成某一系统任务的一个函数,该任务不像当前任务这样紧迫,可以完成了当前任务再来延迟调用。Windows安排了一个DPC级,比设备驱动IRQL低,让设备驱动ISR执行最少最必要的工作来响应设备,将数据传输等非时间紧迫的中断处理活动推迟到DPC级别来执行。
DPC Object,内核控制对象,设备驱动程序和其他系统代码可见。DPC Object包含处理DPC中断时将要调用的系统函数地址。等待执行的DPC例程存储在内核管理队列中:DPC队列。DPC优先级默认是中级,低级和中级会被内核放在DPC队列末尾,高级DPC则插在队列前端。
Windows内核把DPC请求队列放在每个CPU的PRCB数据结构中:
2: kd> dt nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
+0x010 LegacyNumber : UChar
+0x011 NestingLevel : UChar
+0x012 BuildType : Uint2B
+0x014 CpuType : Char
+0x015 CpuID : Char
+0x016 CpuStep : Uint2B
+0x016 CpuStepping : UChar
+0x017 CpuModel : UChar
+0x018 ProcessorState : _KPROCESSOR_STATE
+0x338 KernelReserved : [16] Uint4B
+0x378 HalReserved : [16] Uint4B
+0x3b8 CFlushSize : Uint4B
+0x3bc CoresPerPhysicalProcessor : UChar
+0x3bd LogicalProcessorsPerCore : UChar
+0x3be PrcbPad0 : [2] UChar
+0x3c0 MHz : Uint4B
+0x3c4 CpuVendor : UChar
+0x3c5 GroupIndex : UChar
+0x3c6 Group : Uint2B
+0x3c8 GroupSetMember : Uint4B
+0x3cc Number : Uint4B
+0x3d0 PrcbPad1 : [72] UChar
+0x418 LockQueue : [17] _KSPIN_LOCK_QUEUE
+0x4a0 NpxThread : Ptr32 _KTHREAD
+0x4a4 InterruptCount : Uint4B
+0x4a8 KernelTime : Uint4B
+0x4ac UserTime : Uint4B
+0x4b0 DpcTime : Uint4B
+0x4b4 DpcTimeCount : Uint4B
+0x4b8 InterruptTime : Uint4B
+0x4bc AdjustDpcThreshold : Uint4B
+0x4c0 PageColor : Uint4B
+0x4c4 DebuggerSavedIRQL : UChar
+0x4c5 NodeColor : UChar
+0x4c6 PrcbPad20 : [2] UChar
+0x4c8 NodeShiftedColor : Uint4B
+0x4cc ParentNode : Ptr32 _KNODE
+0x4d0 SecondaryColorMask : Uint4B
+0x4d4 DpcTimeLimit : Uint4B
+0x4d8 PrcbPad21 : [2] Uint4B
+0x4e0 CcFastReadNoWait : Uint4B
+0x4e4 CcFastReadWait : Uint4B
+0x4e8 CcFastReadNotPossible : Uint4B
+0x4ec CcCopyReadNoWait : Uint4B
+0x4f0 CcCopyReadWait : Uint4B
+0x4f4 CcCopyReadNoWaitMiss : Uint4B
+0x4f8 MmSpinLockOrdering : Int4B
+0x4fc IoReadOperationCount : Int4B
+0x500 IoWriteOperationCount : Int4B
+0x504 IoOtherOperationCount : Int4B
+0x508 IoReadTransferCount : _LARGE_INTEGER
+0x510 IoWriteTransferCount : _LARGE_INTEGER
+0x518 IoOtherTransferCount : _LARGE_INTEGER
+0x520 CcFastMdlReadNoWait : Uint4B
+0x524 CcFastMdlReadWait : Uint4B
+0x528 CcFastMdlReadNotPossible : Uint4B
+0x52c CcMapDataNoWait : Uint4B
+0x530 CcMapDataWait : Uint4B
+0x534 CcPinMappedDataCount : Uint4B
+0x538 CcPinReadNoWait : Uint4B
+0x53c CcPinReadWait : Uint4B
+0x540 CcMdlReadNoWait : Uint4B
+0x544 CcMdlReadWait : Uint4B
+0x548 CcLazyWriteHotSpots : Uint4B
+0x54c CcLazyWriteIos : Uint4B
+0x550 CcLazyWritePages : Uint4B
+0x554 CcDataFlushes : Uint4B
+0x558 CcDataPages : Uint4B
+0x55c CcLostDelayedWrites : Uint4B
+0x560 CcFastReadResourceMiss : Uint4B
+0x564 CcCopyReadWaitMiss : Uint4B
+0x568 CcFastMdlReadResourceMiss : Uint4B
+0x56c CcMapDataNoWaitMiss : Uint4B
+0x570 CcMapDataWaitMiss : Uint4B
+0x574 CcPinReadNoWaitMiss : Uint4B
+0x578 CcPinReadWaitMiss : Uint4B
+0x57c CcMdlReadNoWaitMiss : Uint4B
+0x580 CcMdlReadWaitMiss : Uint4B
+0x584 CcReadAheadIos : Uint4B
+0x588 KeAlignmentFixupCount : Uint4B
+0x58c KeExceptionDispatchCount : Uint4B
+0x590 KeSystemCalls : Uint4B
+0x594 AvailableTime : Uint4B
+0x598 PrcbPad22 : [2] Uint4B
+0x5a0 PPLookasideList : [16] _PP_LOOKASIDE_LIST
+0x620 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
+0xf20 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
+0x1820 PacketBarrier : Uint4B
+0x1824 ReverseStall : Int4B
+0x1828 IpiFrame : Ptr32 Void
+0x182c PrcbPad3 : [52] UChar
+0x1860 CurrentPacket : [3] Ptr32 Void
+0x186c TargetSet : Uint4B
+0x1870 WorkerRoutine : Ptr32 void
+0x1874 IpiFrozen : Uint4B
+0x1878 PrcbPad4 : [40] UChar
+0x18a0 RequestSummary : Uint4B
+0x18a4 SignalDone : Ptr32 _KPRCB
+0x18a8 PrcbPad50 : [56] UChar
+0x18e0 DpcData : [2] _KDPC_DATA
+0x1908 DpcStack : Ptr32 Void
+0x190c MaximumDpcQueueDepth : Int4B
+0x1910 DpcRequestRate : Uint4B
+0x1914 MinimumDpcRate : Uint4B
+0x1918 DpcLastCount : Uint4B
+0x191c PrcbLock : Uint4B
+0x1920 DpcGate : _KGATE
+0x1930 ThreadDpcEnable : UChar
+0x1931 QuantumEnd : UChar
+0x1932 DpcRoutineActive : UChar
+0x1933 IdleSchedule : UChar
+0x1934 DpcRequestSummary : Int4B
+0x1934 DpcRequestSlot : [2] Int2B
+0x1934 NormalDpcState : Int2B
+0x1936 DpcThreadActive : Pos 0, 1 Bit
+0x1936 ThreadDpcState : Int2B
+0x1938 TimerHand : Uint4B
+0x193c LastTick : Uint4B
+0x1940 MasterOffset : Int4B
+0x1944 PrcbPad41 : [2] Uint4B
+0x194c PeriodicCount : Uint4B
+0x1950 PeriodicBias : Uint4B
+0x1958 TickOffset : Uint8B
+0x1960 TimerTable : _KTIMER_TABLE
+0x31a0 CallDpc : _KDPC
+0x31c0 ClockKeepAlive : Int4B
+0x31c4 ClockCheckSlot : UChar
+0x31c5 ClockPollCycle : UChar
+0x31c6 PrcbPad6 : [2] UChar
+0x31c8 DpcWatchdogPeriod : Int4B
+0x31cc DpcWatchdogCount : Int4B
+0x31d0 ThreadWatchdogPeriod : Int4B
+0x31d4 ThreadWatchdogCount : Int4B
+0x31d8 KeSpinLockOrdering : Int4B
+0x31dc PrcbPad70 : [1] Uint4B
+0x31e0 WaitListHead : _LIST_ENTRY
+0x31e8 WaitLock : Uint4B
+0x31ec ReadySummary : Uint4B
+0x31f0 QueueIndex : Uint4B
+0x31f4 DeferredReadyListHead : _SINGLE_LIST_ENTRY
+0x31f8 StartCycles : Uint8B
+0x3200 CycleTime : Uint8B
+0x3208 HighCycleTime : Uint4B
+0x320c PrcbPad71 : Uint4B
+0x3210 PrcbPad72 : [2] Uint8B
+0x3220 DispatcherReadyListHead : [32] _LIST_ENTRY
+0x3320 ChainedInterruptList : Ptr32 Void
+0x3324 LookasideIrpFloat : Int4B
+0x3328 MmPageFaultCount : Int4B
+0x332c MmCopyOnWriteCount : Int4B
+0x3330 MmTransitionCount : Int4B
+0x3334 MmCacheTransitionCount : Int4B
+0x3338 MmDemandZeroCount : Int4B
+0x333c MmPageReadCount : Int4B
+0x3340 MmPageReadIoCount : Int4B
+0x3344 MmCacheReadCount : Int4B
+0x3348 MmCacheIoCount : Int4B
+0x334c MmDirtyPagesWriteCount : Int4B
+0x3350 MmDirtyWriteIoCount : Int4B
+0x3354 MmMappedPagesWriteCount : Int4B
+0x3358 MmMappedWriteIoCount : Int4B
+0x335c CachedCommit : Uint4B
+0x3360 CachedResidentAvailable : Uint4B
+0x3364 HyperPte : Ptr32 Void
+0x3368 PrcbPad8 : [4] UChar
+0x336c VendorString : [13] UChar
+0x3379 InitialApicId : UChar
+0x337a LogicalProcessorsPerPhysicalProcessor : UChar
+0x337b PrcbPad9 : [5] UChar
+0x3380 FeatureBits : Uint4B
+0x3388 UpdateSignature : _LARGE_INTEGER
+0x3390 IsrTime : Uint8B
+0x3398 RuntimeAccumulation : Uint8B
+0x33a0 PowerState : _PROCESSOR_POWER_STATE
+0x3468 DpcWatchdogDpc : _KDPC
+0x3488 DpcWatchdogTimer : _KTIMER
+0x34b0 WheaInfo : Ptr32 Void
+0x34b4 EtwSupport : Ptr32 Void
+0x34b8 InterruptObjectPool : _SLIST_HEADER
+0x34c0 HypercallPageList : _SLIST_HEADER
+0x34c8 HypercallPageVirtual : Ptr32 Void
+0x34cc VirtualApicAssist : Ptr32 Void
+0x34d0 StatisticsPage : Ptr32 Uint8B
+0x34d4 RateControl : Ptr32 Void
+0x34d8 Cache : [5] _CACHE_DESCRIPTOR
+0x3514 CacheCount : Uint4B
+0x3518 CacheProcessorMask : [5] Uint4B
+0x352c PackageProcessorSet : _KAFFINITY_EX
+0x3538 PrcbPad91 : [1] Uint4B
+0x353c CoreProcessorSet : Uint4B
+0x3540 TimerExpirationDpc : _KDPC
+0x3560 SpinLockAcquireCount : Uint4B
+0x3564 SpinLockContentionCount : Uint4B
+0x3568 SpinLockSpinCount : Uint4B
+0x356c IpiSendRequestBroadcastCount : Uint4B
+0x3570 IpiSendRequestRoutineCount : Uint4B
+0x3574 IpiSendSoftwareInterruptCount : Uint4B
+0x3578 ExInitializeResourceCount : Uint4B
+0x357c ExReInitializeResourceCount : Uint4B
+0x3580 ExDeleteResourceCount : Uint4B
+0x3584 ExecutiveResourceAcquiresCount : Uint4B
+0x3588 ExecutiveResourceContentionsCount : Uint4B
+0x358c ExecutiveResourceReleaseExclusiveCount : Uint4B
+0x3590 ExecutiveResourceReleaseSharedCount : Uint4B
+0x3594 ExecutiveResourceConvertsCount : Uint4B
+0x3598 ExAcqResExclusiveAttempts : Uint4B
+0x359c ExAcqResExclusiveAcquiresExclusive : Uint4B
+0x35a0 ExAcqResExclusiveAcquiresExclusiveRecursive : Uint4B
+0x35a4 ExAcqResExclusiveWaits : Uint4B
+0x35a8 ExAcqResExclusiveNotAcquires : Uint4B
+0x35ac ExAcqResSharedAttempts : Uint4B
+0x35b0 ExAcqResSharedAcquiresExclusive : Uint4B
+0x35b4 ExAcqResSharedAcquiresShared : Uint4B
+0x35b8 ExAcqResSharedAcquiresSharedRecursive : Uint4B
+0x35bc ExAcqResSharedWaits : Uint4B
+0x35c0 ExAcqResSharedNotAcquires : Uint4B
+0x35c4 ExAcqResSharedStarveExclusiveAttempts : Uint4B
+0x35c8 ExAcqResSharedStarveExclusiveAcquiresExclusive : Uint4B
+0x35cc ExAcqResSharedStarveExclusiveAcquiresShared : Uint4B
+0x35d0 ExAcqResSharedStarveExclusiveAcquiresSharedRecursive : Uint4B
+0x35d4 ExAcqResSharedStarveExclusiveWaits : Uint4B
+0x35d8 ExAcqResSharedStarveExclusiveNotAcquires : Uint4B
+0x35dc ExAcqResSharedWaitForExclusiveAttempts : Uint4B
+0x35e0 ExAcqResSharedWaitForExclusiveAcquiresExclusive : Uint4B
+0x35e4 ExAcqResSharedWaitForExclusiveAcquiresShared : Uint4B
+0x35e8 ExAcqResSharedWaitForExclusiveAcquiresSharedRecursive : Uint4B
+0x35ec ExAcqResSharedWaitForExclusiveWaits : Uint4B
+0x35f0 ExAcqResSharedWaitForExclusiveNotAcquires : Uint4B
+0x35f4 ExSetResOwnerPointerExclusive : Uint4B
+0x35f8 ExSetResOwnerPointerSharedNew : Uint4B
+0x35fc ExSetResOwnerPointerSharedOld : Uint4B
+0x3600 ExTryToAcqExclusiveAttempts : Uint4B
+0x3604 ExTryToAcqExclusiveAcquires : Uint4B
+0x3608 ExBoostExclusiveOwner : Uint4B
+0x360c ExBoostSharedOwners : Uint4B
+0x3610 ExEtwSynchTrackingNotificationsCount : Uint4B
+0x3614 ExEtwSynchTrackingNotificationsAccountedCount : Uint4B
+0x3618 Context : Ptr32 _CONTEXT
+0x361c ContextFlags : Uint4B
+0x3620 ExtendedState : Ptr32 _XSAVE_AREA
DPC请求队列数据结构:
2: kd> dt nt!_KDPC_DATA
+0x000 DpcListHead : _LIST_ENTRY
+0x008 DpcLock : Uint4B
+0x00c DpcQueueDepth : Int4B
+0x010 DpcCount : Uint4B
DEVICE_OBJECT中的DPC,用来设置本设备有关对象的DPC函数信息:
2: kd> dt nt!_KDPC
+0x000 Type : UChar
+0x001 Importance : UChar
+0x002 Number : Uint2B
+0x004 DpcListEntry : _LIST_ENTRY
+0x00c DeferredRoutine : Ptr32 void
+0x010 DeferredContext : Ptr32 Void
+0x014 SystemArgument1 : Ptr32 Void
+0x018 SystemArgument2 : Ptr32 Void
+0x01c DpcData : Ptr32 Void
当处理器IRQL从DPC/Dispatch或更高级降到更低级时,内核会处理DPC。此时将IRQL仍然保持在DPC/Dispatch级别上,取出当前处理器队列中的DPC对象,以此调用每一个DPC函数来处理DPC队列,直到DPC队列为空,然后降低IRQL,让正常的线程执行过程继续进行。内核通常通过一个DPC/Dispatch级别中断来激发DPC队列的清空,以此影响系统行为。
DPC中断产生规则:
DPC Priority | DPC Targeted at ISR's Processor | DPC Targeted at Another Processor |
Low | DPC queue length exceeds maximum DPC queue length or DPC request rate is less than minimum DPC request rate | DPC queue length exceeds maximum DPC queue length or System is idle |
Medium | Always | DPC queue length exceeds maximum DPC queue length or System is idle |
High | Always | Always |
DPC主要为设备驱动程序而提供,内核使用DPC来处理到期事件。系统时钟的每个tick都会产生Clock IRQL,更新系统时间,递减当前线程已运行时间的计数器,当计数器归0,则内核需要在DPC/Dispatch IRQL级别重新调度该处理器:插入一个DPC至队列,结束当前工作并降低IRQL。
异步过程调用Asynchronous Procedure Call (APC):提供了再特定用户线程环境中执行用户程序和系统代码的途径。可以访问对象资源,等待对象句柄,引发页面错误,调用系统服务。
APC Object:内核控制对象,每个线程有自己的APC队列。内核接到请求要将一个APC排队时,将APC插入将来执行此APC例程的那个线程的APC队列中,然后请求APC级别中断,当该线程开始运行时,就会执行此APC。
内核模式APC和用户模式APC:内核模式不要求从目标线程获得许可就可以运行在改线程的环境中,用户模式APC必须先获得许可。内核模式APC分为两种:normal, special. 内核模式APC运行在APC级别上,用户模式APC运行在Passive级别上。
异常分发:
异常是由正在执行的程序直接产生的条件,Windows通过结构化异常处理structured exception handling,使得应用程序在异常条件触发时得到控制,修正条件然后返回程序执行处,或向系统报告异常不可识别。异常处理时一种系统机制,并不与特定语言相关。X86平台上的所有异常都有预定义中断号,对应于IDT的表项,每个表项对应特定异常的陷阱处理器。IDT表中前部分表项用于异常,后部分用于硬件中断。简单异常可以通过陷阱处理器解决,否则通过异常分发器(exception dispatcher)内核模块调用相应的异常处理器。
Interrupt Number | Exception |
0 | Divide Error |
1 | DEBUG TRAP |
2 | NMI/NPX Error |
3 | Breakpoint |
4 | Overflow |
5 | BOUND/Print Screen |
6 | Invalid Opcode |
7 | NPX Not Available |
8 | Double Exception |
9 | NPX Segment Overrun |
A | Invalid Task State Segment (TSS) |
B | Segment Not Present |
C | Stack Fault |
D | General Protection |
E | Page Fault |
F | Intel Reserved |
10 | Floating Point |
11 | Alignment Check |
内核可以以透明于用户程序的方式捕获并处理某些异常,如调试陷阱;或直接回到用户模式,如算术溢出等,由基于帧的异常处理器(frame-based exception handler)来处理。Stack frame, 组成callstack,每个stack frame包含未完成运行的函数信息,包括参数传递、返回值、寄存器信息等。当一个过程被调用,一个stack frame被压入栈中,一个stack frame可以关联一个或多个异常处理器。发生一个异常,首先查找当前栈帧关联的异常处理器,若没找到,继续查找与上一个栈帧关联的异常处理器,直到找到为止,若最终没有找到,则调用内核默认的异常处理器。异常发生时,控制权被交给陷阱处理器,创建一个trap frame陷阱帧,使得在异常解决后系统可以从停止处继续运行。
未处理异常:所有Windows异常都有一个异常处理器,位于栈顶,处理所有未处理异常,该异常处理器在windows内部的进程启动函数(调用可执行映像的主入口函数)或线程启动函数(调用由用户指定的线程启动例程,因此系统中所有进程的0号线程的启动地址相同)中声明的。
Windows未处理异常过滤器在线程异常没有被处理时调用,从而提供系统统一的行为和方法。HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug.
Windows Error Reporting,Windows错误报告:
检查HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto是否为0或debugger字符串为Drwtsn32
加载Faulttrep.dll至失败进程,调用ReportFault函数
ReportFault函数检查HKLM\Software\Microsoft\PCHealth\ErrorReporting配置,确定是否需要报告,如何报告,一般创建一个进程运行Dwwin.exe显示报告
创建一个进程以运行定义的系统调试器
系统服务分发:
每当执行一条专门分配给系统服务分发的指令时,系统服务分发功能就会被触发,取决于它执行时所在的处理器。
Pentium II之前的x86处理器:int 0x2e指令,会产生一个陷阱,填充IDT的第46号表项,指向系统服务分发器,陷阱使得执行线程转换到内核模式中,进入系统分发服务器,EAX指明所请求的系统服务号,EBX指向调用者传递给该系统服务的参数列表。
Pentium II之后的x86处理器,使用sysenter指令。Windows在引导时刻将内核的系统服务分发器地址保存在于该指令相关联的一个寄存器中。EAX传递系统服务号,EDX指向调用者的参数列表。使用sysexit返回用户模式。若单步模式开启,则使用iretd指令。
K6之后的32位处理器,使用syscall指令,EAX传递系统服务号,调用者的参数保存在栈中。使用sysret返回。
X64处理器,使用syscall指令进行系统服务分发,EAX传递系统调用号,前4个参数放在寄存器中,其他参数放在栈中。
IA64处理器,使用epc指令进行系统服务分发,前8个系统调用参数通过寄存器来传递,其他参数用栈来传递。
内核利用传递进来的参数,找到系统服务分发表中的系统服务信息。
系统分发服务表(system service dispatcher)KiSystemService将调用者的参数从线程的用户模式拷贝到内核模式栈中,然后执行系统服务。
每个线程都有一个指针指向它的系统服务表,windows有两个内置服务表,最多可以支持4个。将32位系统服务号的12、13位解释成一个表索引,低12位用于在该表中索引。KeServiceDescriptorTable中定义了Ntosrknl.exe中实现的执行体系统服务,KeServiceDescriptorTableShadow中相同,但多了win32k.sys中实现的Windows USER和GDI服务。KeAddSystemServiceTable使得win32k.sys和其他设备驱动程序可以添加系统服务
No comments:
Post a Comment