DPC是Deferred Procedure Call,推迟的过程调用。内核中每个CPU都有一个DPC请求队列,DPC请求队列在每个CPU的PRCB数据结构中
设备对象的数据结构DEVICE_OBJECT中有个成分Dpc,它是KDPC数据结构,用来设置有关本设备对象的DPC函数的信息。
ntdll!_KDPC +0x000 Type : Int2B +0x002 Number : UChar +0x003 Importance : UChar +0x004 DpcListEntry : _LIST_ENTRY +0x00c DeferredRoutine : Ptr32 void +0x010 DeferredContext : Ptr32 Void +0x014 SystemArgument1 : Ptr32 Void +0x018 SystemArgument2 : Ptr32 Void +0x01c Lock : Ptr32 Uint4B
设备对象在初始化时通过KeInitializeDpc()设置好它的KDPC数据结构。当需要执行DPC函数时,通过KeInsertQueueDpc()提出DPC请求,把具体的KPC结构挂入PRCB中的DPC请求队列。如果将DPC请求挂入了队列,通过HalRequestSoftwareInterrupt()将PCR中的相应标志位设置为TRUE,表示要求扫描DPC请求队列。
当CPU的运行级别从DISPATCH_LEVEL或以上降低到DISPATCH_LEVEL以下时,如果有扫描DPC请求队列的要求存在,内核就会扫描DPC请求队列并执行DPC函数。
Windows内核提供了一个专门的线程来处理这些DPC函数。线程通过工作队列接受来自其他线程的工作请求。工作队列一共有3个,组成工作队列数组
EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
如有需要,别的线程可以准备好一个工作请求WORK_QUEUE_ITEM数据结构,将其挂入ExWorkerQueue[]中的某个队列。准备函数是ExInitializeWorkItem(),通过ExQueueWorkItem()将其挂入某个工作队列